Unity升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件

Posted 萧然CS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件相关的知识,希望对你有一定的参考价值。

实现功能:

  • 自动创建继承ScriptableObject的C#数据类,每条Excel的数据,都有对应的字段的Get函数; 

 

  • 自动创建每个Excel的Asset生成类和生成函数,用于自动生成Asset文件

  • 使用Asset生成类自动序列化Excel数据到Asset文件,可直接在项目运行时加载使用

实现原理:

Excel配置格式:

  • 第1行对应特殊标记(可以设置有效性,指定要创建的文件)
  • 第2行对应中文说明(作为第3行字段的注释)
  • 第3行对应字段名称(自动创建的字段名称)
  • 第4行对应字段类型(自动创建的字段类型,与字段名称一一对应)
  • 第5行及以后对应字段值(所有数据,以行为单位解析、保存数据)
  • 第一列固定字段为"id",是代码中索引每行数据的Key

Excel注释操作:

  • 字段名称行,每个字段单元格内容前加"//",可以注释该字段,不会解析生成到C#类;
  • 第一列的单元格内容前加"//",可以注释一行数据,不会保存到Asset文件中;
  • 注释可以用于添加说明行,或剔除指定无用数据。

生成的C#类格式:

行数据类,对应每一行数据:

[Serializable]
public class TestConfigExcelItem : ExcelItemBase

	/// <summary>
	/// 数据id
	/// </summary>>
	public int id;
	/// <summary>
	/// 字符串
	/// </summary>>
	public string testString;
	/// <summary>
	/// Int
	/// </summary>>
	public int testInt;
	/// <summary>
	/// Float
	/// </summary>>
	public float testFloat;

完整数据类,包含所有行的数据、初始化函数、Get函数:

public class TestConfigExcelData : ExcelDataBase<TestConfigExcelItem>

	public TestConfigExcelItem[] items;

	public Dictionary<int,TestConfigExcelItem> itemDic = new Dictionary<int,TestConfigExcelItem>();

	public void Init()
	
		itemDic.Clear();
		if(items != null && items.Length > 0)
		
			for(int i = 0; i < items.Length; i++)
			
				itemDic.Add(items[i].id, items[i]);
			
		
	

	public TestConfigExcelItem GetTestConfigExcelItem(int id)
	
		if(itemDic.ContainsKey(id))
			return itemDic[id];
		else
			return null;
	
	#region --- Get Method ---

	public string GetTestString(int id)
	
		var item = GetTestConfigExcelItem(id);
		if(item == null)
			return default;
		return item.testString;
	

	// ··· ···

	#endregion

目前支持的数据结构:

字符串testString字符串数组testStringArray字符串二维数组testStringArray2
InttestIntInt数组testIntArrayInt二维数组testIntArray2
FloattestFloatFloat数组testFloatArrayFloat二维数组testFloatArray2
BooltestBoolBool数组testBoolArrayBool二维数组testBoolArray2
Enum|枚举名(或枚举值)testEnumEnum数组testEnumArrayEnum二维数组不支持
Vector2testVector2Vector2数组testVector2ArrayVector2二维数组testVector2Array2
Vector3testVector3Vector3数组testVector3ArrayVector3二维数组testVector3Array2
Vector2InttestVector2IntVector2Int数组testVector2IntArrayVector2Int二维数组testVector2IntArray2
Vector3InttestVector3IntVector3Int数组testVector3IntArrayVector3Int二维数组testVector3IntArray2
ColortestColorColor数组testColorArrayColor二维数组testColorArray2
Color32testColor32Color32数组testColor32ArrayColor32二维数组testColor32Array2

因为Unity不能序列化二维数组,这里改成一维数组+结构体的方式实现:

[Serializable]
public struct StringArr

    public string[] array;


//二维数组表示方式: StringArr[]

Asset数据文件:

在自动生成数据的C#类时,会同步生成Asset文件的创建类,用于自动创建Asset文件并序列化数据。

优点:

  • 数据修改后只需要重新一键生成即可
  • 每个Excel对应一个类,使用灵活,对Excel限制少
  • 自动创建C#类,不需要对每个Excel手动写代码,每条数据对应字段,不需要拆箱装修
  • 自动创建ScriptableObject的Asset文件,自动序列化数据,方便查看,可以手动修改调整,不需要每次改动都在Excel里操作
  • 在游戏内直接读取Asset的ScriptableObject子类,不需要额外操作,业务层直接调取数据字段

使用方法:

  • 按照标准格式配置Excel
  • 一键生成C#类、Asset文件
  • 项目运行时加载Asset资源,调用Init初始化,Get函数获取对应字段值即可

完整代码:

扩展Unity编辑器窗口:

using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections.Generic;
using System.Linq;

public class BuildExcelWindow : EditorWindow

    [MenuItem("MyTools/Excel Window",priority = 100)]
    public static void ShowReadExcelWindow()
    
        BuildExcelWindow window = GetWindow<BuildExcelWindow>(true);
        window.Show();
        window.minSize = new Vector2(475,475);
    

    //Excel读取路径,绝对路径,放在Assets同级路径
    private static string excelReadAbsolutePath;

    //自动生成C#类文件路径,绝对路径
    private static string scriptSaveAbsolutePath;
    private static string scriptSaveRelativePath;
    //自动生成Asset文件路径,相对路径
    private static string assetSaveRelativePath;

    private List<string> fileNameList = new List<string>();
    private List<string> filePathList = new List<string>();

    private void Awake()
    
        titleContent.text = "Excel配置表读取";

        excelReadAbsolutePath = Application.dataPath.Replace("Assets","Excel");
        scriptSaveAbsolutePath = Application.dataPath + CheckEditorPath("/Script/Excel/AutoCreateCSCode");
        scriptSaveRelativePath = CheckEditorPath("Assets/Script/Excel/AutoCreateCSCode");
        assetSaveRelativePath = CheckEditorPath("Assets/AssetData/Excel/AutoCreateAsset");
    

    private void OnEnable()
    
        RefreshExcelFile();
    

    private void OnDisable()
    
        fileNameList.Clear();
        filePathList.Clear();
    

    private Vector2 scrollPosition = Vector2.zero;
    private void OnGUI()
    
        GUILayout.Space(10);

        scrollPosition = GUILayout.BeginScrollView(scrollPosition,GUILayout.Width(position.width),GUILayout.Height(position.height));

        //展示路径
        GUILayout.BeginHorizontal(GUILayout.Height(20));
        if(GUILayout.Button("Excel读取路径",GUILayout.Width(100)))
        
            EditorUtility.OpenWithDefaultApp(excelReadAbsolutePath);
            Debug.Log(excelReadAbsolutePath);
        
        if(GUILayout.Button("Script保存路径",GUILayout.Width(100)))
        
            SelectObject(scriptSaveRelativePath);
        
        if(GUILayout.Button("Asset保存路径",GUILayout.Width(100)))
        
            SelectObject(assetSaveRelativePath);
        
        GUILayout.EndHorizontal();

        GUILayout.Space(5);

        //Excel列表

        GUILayout.Label("Excel列表:");
        for(int i = 0; i < fileNameList.Count; i++)
        
            GUILayout.BeginHorizontal("Box",GUILayout.Height(40));

            GUILayout.Label($"i:","Titlebar Foldout",GUILayout.Width(30),GUILayout.Height(35));
            GUILayout.Box(fileNameList[i],"MeTransitionBlock",GUILayout.MinWidth(200),GUILayout.Height(35));
            GUILayout.Space(10);

            //生成CS代码
            if(GUILayout.Button("Create Script",GUILayout.Width(100),GUILayout.Height(30)))
            
                ExcelDataReader.ReadOneExcelToCode(filePathList[i],scriptSaveAbsolutePath);
            
            //生成Asset文件
            if(GUILayout.Button("Create Asset",GUILayout.Width(100),GUILayout.Height(30)))
            
                ExcelDataReader.CreateOneExcelAsset(filePathList[i],assetSaveRelativePath);
            

            GUILayout.EndHorizontal();
            GUILayout.Space(5);
        
        GUILayout.Space(10);

        //一键处理所有Excel

        GUILayout.Label("一键操作:");
        GUILayout.BeginHorizontal("Box",GUILayout.Height(40));

        GUILayout.Label("all","Titlebar Foldout",GUILayout.Width(30),GUILayout.Height(35));
        GUILayout.Box("All Excel","MeTransitionBlock",GUILayout.MinWidth(200),GUILayout.Height(35));
        GUILayout.Space(10);

        if(GUILayout.Button("Create Script",GUILayout.Width(100),GUILayout.Height(30)))
        
            ExcelDataReader.ReadAllExcelToCode(excelReadAbsolutePath,scriptSaveAbsolutePath);
        
        if(GUILayout.Button("Create Asset",GUILayout.Width(100),GUILayout.Height(30)))
        
            ExcelDataReader.CreateAllExcelAsset(excelReadAbsolutePath,assetSaveRelativePath);
        
        GUILayout.EndHorizontal();

        //
        GUILayout.Space(20);
        //
        GUILayout.EndScrollView();
    

    //读取指定路径下的Excel文件名
    private void RefreshExcelFile()
    
        fileNameList.Clear();
        filePathList.Clear();

        if(!Directory.Exists(excelReadAbsolutePath))
        
            Debug.LogError("无效路径:" + excelReadAbsolutePath);
            return;
        
        string[] excelFileFullPaths = Directory.GetFiles(excelReadAbsolutePath,"*.xlsx");

        if(excelFileFullPaths == null || excelFileFullPaths.Length == 0)
        
            Debug.LogError(excelReadAbsolutePath + "路径下没有找到Excel文件");
            return;
        

        filePathList.AddRange(excelFileFullPaths);
        for(int i = 0; i < filePathList.Count; i++)
        
            fileNameList.Add(Path.GetFileName(filePathList[i]));
        
        Debug.Log("找到Excel文件:" + fileNameList.Count + "个");
    

    private void SelectObject(string targetPath)
    
        Object targetObj = AssetDatabase.LoadAssetAtPath<Object>(targetPath);
        EditorGUIUtility.PingObject(targetObj);
        Selection.activeObject = targetObj;
        Debug.Log(targetPath);
    

    private static string CheckEditorPath(string path)
    
#if UNITY_EDITOR_WIN
        return path.Replace("/","\\\\");
#elif UNITY_EDITOR_OSX
        return path.Replace("\\\\","/");
#else
        return path;
#endif
    

Excel数据读取类: 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using Excel;
using System.Reflection;
using System;
using System.Linq;

public class ExcelDataReader

    //Excel第1行对应特殊标记
    private const int specialSignRow = 0;
    //Excel第2行对应中文说明
    private const int excelNodeRow = 1;
    //Excel第3行对应字段名称
    private const int excelNameRow = 2;
    //Excel第4行对应字段类型
    private const int excelTypeRow = 3;
    //Excel第5行及以后对应字段值
    private const int excelDataRow = 4;

    //标记注释行/列
    private const string annotationSign = "//";

    #region --- Read Excel ---

    //创建Excel对应的C#类
    public static void ReadAllExcelToCode(string allExcelPath,string codeSavePath)
    
        //读取所有Excel文件
        //指定目录中与指定的搜索模式和选项匹配的文件的完整名称(包含路径)的数组;如果未找到任何文件,则为空数组。
        string[] excelFileFullPaths = Directory.GetFiles(allExcelPath,"*.xlsx");
        if(excelFileFullPaths == null || excelFileFullPaths.Length == 0)
        
            Debug.Log("Excel file count == 0");
            return;
        
        //遍历所有Excel,创建C#类
        for(int i = 0; i < excelFileFullPaths.Length; i++)
        
            ReadOneExcelToCode(excelFileFullPaths[i],codeSavePath);
        
    

    //创建Excel对应的C#类
    public static void ReadOneExcelToCode(string excelFullPath,string codeSavePath)
    
        //解析Excel获取中间数据
        ExcelMediumData excelMediumData = CreateClassCodeByExcelPath(excelFullPath);
        if(excelMediumData == null)
        
            Debug.LogError($"读取Excel失败 : excelFullPath");
            return;
        
        if(!excelMediumData.isValid)
        
            Debug.LogError($"读取Excel失败,Excel标记失效 : excelMediumData.excelName");
            return;
        

        if(!excelMediumData.isCreateCSharp && !excelMediumData.isCreateAssignment)
        
            Debug.LogError($"读取Excel失败,Excel不允许生成CSCode : excelMediumData.excelName");
            return;
        

        //根据数据生成C#脚本
        string classCodeStr = ExcelCodeCreater.CreateCodeStrByExcelData(excelMediumData);
        if(string.IsNullOrEmpty(classCodeStr))
        
            Debug.LogError($"解析Excel失败 : excelMediumData.excelName");
            return;
        

        //检查导出路径
        if(!Directory.Exists(codeSavePath))
            Directory.CreateDirectory(codeSavePath);
        //类名
        string codeFileName = excelMediumData.excelName + "ExcelData";
        //写文件,生成CS类文件
        StreamWriter sw = new StreamWriter($"codeSavePath/codeFileName.cs");
        sw.WriteLine(classCodeStr);
        sw.Close();
        //
        UnityEditor.AssetDatabase.SaveAssets();
        UnityEditor.AssetDatabase.Refresh();
        //
        Debug.Log($"生成Excel的CS成功 : excelMediumData.excelName");
    

    #endregion

    #region --- Create Asset ---

    //创建Excel对应的Asset数据文件
    public static void CreateAllExcelAsset(string allExcelPath,string assetSavePath)
    
        //读取所有Excel文件
        //指定目录中与指定的搜索模式和选项匹配的文件的完整名称(包含路径)的数组;如果未找到任何文件,则为空数组。
        string[] excelFileFullPaths = Directory.GetFiles(allExcelPath,"*.xlsx");
        if(excelFileFullPaths == null || excelFileFullPaths.Length == 0)
        
            Debug.Log("Excel file count == 0");
            return;
        
        //遍历所有Excel,创建Asset
        for(int i = 0; i < excelFileFullPaths.Length; i++)
        
            CreateOneExcelAsset(excelFileFullPaths[i],assetSavePath);
        
    

    //创建Excel对应的Asset数据文件
    public static void CreateOneExcelAsset(string excelFullPath,string assetSavePath)
    
        //解析Excel获取中间数据
        ExcelMediumData excelMediumData = CreateClassCodeByExcelPath(excelFullPath);
        if(excelMediumData == null)
        
            Debug.LogError($"读取Excel失败 : excelFullPath");
            return;
        
        if(!excelMediumData.isValid)
        
            Debug.LogError($"读取Excel失败,Excel标记失效 : excelMediumData.excelName");
            return;
        

        if(!excelMediumData.isCreateAsset)
        
            Debug.LogError($"读取Excel失败,Excel不允许生成Asset : excelMediumData.excelName");
            return;
        

        获取当前程序集
        //Assembly assembly = Assembly.GetExecutingAssembly();
        创建类的实例,返回为 object 类型,需要强制类型转换,assembly.CreateInstance("类的完全限定名(即包括命名空间)");
        //object class0bj = assembly.CreateInstance(excelMediumData.excelName + "Assignment",true);

        //必须遍历所有程序集来获得类型。当前在Assembly-CSharp-Editor中,目标类型在Assembly-CSharp中,不同程序将无法获取类型
        Type assignmentType = null;
        string assetAssignmentName = excelMediumData.excelName + "AssetAssignment";
        foreach(var asm in AppDomain.CurrentDomain.GetAssemblies())
        
            //查找目标类型
            Type tempType = asm.GetType(assetAssignmentName);
            if(tempType != null)
            
                assignmentType = tempType;
                break;
            
        
        if(assignmentType == null)
        
            Debug.LogError($"创界Asset失败,未找到Asset生成类 : excelMediumData.excelName");
            return;
        

        //反射获取方法
        MethodInfo methodInfo = assignmentType.GetMethod("CreateAsset");
        if(methodInfo == null)
        
            if(assignmentType == null)
            
                Debug.LogError($"创界Asset失败,未找到Asset创建函数 : excelMediumData.excelName");
                return;
            
        

        methodInfo.Invoke(null,new object[]  excelMediumData,assetSavePath );
        //创建Asset文件成功
        Debug.Log($"生成Excel的Asset成功 : excelMediumData.excelName");
    

    #endregion

    #region --- private ---

    //解析Excel,创建中间数据
    private static ExcelMediumData CreateClassCodeByExcelPath(string excelFileFullPath)
    
        if(string.IsNullOrEmpty(excelFileFullPath))
            return null;

        excelFileFullPath = excelFileFullPath.Replace("\\\\","/");
        //读取Excel
        FileStream stream = File.Open(excelFileFullPath,FileMode.Open,FileAccess.Read);
        if(stream == null)
            return null;
        //解析Excel
        IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
        //无效Excel
        if(excelReader == null || !excelReader.IsValid)
        
            Debug.Log("Invalid excel : " + excelFileFullPath);
            return null;
        

        Debug.Log("开始解析Excel : " + excelReader.Name);

        //记录Excel数据
        ExcelMediumData excelMediumData = new ExcelMediumData();

        //Excel名字
        excelMediumData.excelName = excelReader.Name;

        //当前遍历的行
        int curRowIndex = 0;
        //开始读取,按行遍历
        while(excelReader.Read())
        
            //这一行没有读取到数据,视为无效行数据
            if(excelReader.FieldCount <= 0)
            
                curRowIndex++;
                continue;
            
            //读取每一行的完整数据
            string[] datas = new string[excelReader.FieldCount];
            for(int j = 0; j < excelReader.FieldCount; ++j)
            
                //可以直接读取指定类型数据,不过只支持有限数据类型,这里统一读取string,然后再数据转化
                //excelReader.GetInt32(j); excelReader.GetFloat(j);

                //读取每一个单元格数据
                datas[j] = excelReader.GetString(j);
            

            switch(curRowIndex)
            
                case specialSignRow:
                    //特殊标记行
                    string specialSignStr = datas[0];
                    if(specialSignStr.Length >= 4)
                    
                        excelMediumData.isValid = specialSignStr[0] == 'T';
                        excelMediumData.isCreateCSharp = specialSignStr[1] == 'T';
                        excelMediumData.isCreateAssignment = specialSignStr[2] == 'T';
                        excelMediumData.isCreateAsset = specialSignStr[3] == 'T';
                    
                    else
                    
                        Debug.LogError("未解析到特殊标记");
                    
                    break;
                case excelNodeRow:
                    //数据注释行
                    excelMediumData.propertyNodeArray = datas;
                    break;
                case excelNameRow:
                    //数据名称行
                    excelMediumData.propertyNameArray = datas;
                    //注释列号
                    for(int i = 0; i < datas.Length; i++)
                    
                        if(string.IsNullOrEmpty(datas[i]) || datas[i].StartsWith(annotationSign))
                            excelMediumData.annotationColList.Add(i);
                    
                    break;
                case excelTypeRow:
                    //数据类型行
                    excelMediumData.propertyTypeArray = datas;
                    break;
                default:
                    //数据内容行
                    excelMediumData.allRowItemList.Add(datas);
                    //注释行号
                    if(string.IsNullOrEmpty(datas[0]) || datas[0].StartsWith(annotationSign))
                        excelMediumData.annotationRowList.Add(excelMediumData.allRowItemList.Count - 1);
                    break;
            
            //
            curRowIndex++;
        

        if(CheckExcelMediumData(ref excelMediumData))
        
            Debug.Log("读取Excel成功");
            return excelMediumData;
        
        else
        
            Debug.LogError("读取Excel失败");
            return null;
        
    

    //校验Excel数据
    private static bool CheckExcelMediumData(ref ExcelMediumData mediumData)
    
        if(mediumData == null)
            return false;

        //检查数据有效性

        if(!mediumData.isValid)
        
            Debug.LogError("Excel被标记无效");
            return false;
        

        if(string.IsNullOrEmpty(mediumData.excelName))
        
            Debug.LogError("Excel名字为空");
            return false;
        

        if(mediumData.propertyNameArray == null || mediumData.propertyNameArray.Length == 0)
        
            Debug.LogError("未解析到数据名称");
            return false;
        
        if(mediumData.propertyTypeArray == null || mediumData.propertyTypeArray.Length == 0)
        
            Debug.LogError("未解析到数据类型");
            return false;
        
        if(mediumData.propertyNameArray.Length != mediumData.propertyTypeArray.Length)
        
            Debug.LogError("数据名称与数据类型数量不一致");
            return false;
        
        if(mediumData.allRowItemList.Count == 0)
        
            Debug.LogError("数据内容为空");
            return false;
        

        if(mediumData.propertyNameArray[0] != "id")
        
            Debug.LogError("第一个字段必须是id字段");
            return false;
        

        return true;
    

    #endregion

C#代码生成类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
using System.Linq;
using System;

public class ExcelCodeCreater


    //创建代码,生成数据C#类
    public static string CreateCodeStrByExcelData(ExcelMediumData excelMediumData)
    
        if(excelMediumData == null)
            return null;

        //行数据类名
        string itemClassName = excelMediumData.excelName + "ExcelItem";
        //整体数据类名
        string dataClassName = excelMediumData.excelName + "ExcelData";

        //开始生成类
        StringBuilder classSource = new StringBuilder();
        classSource.AppendLine("/*Auto Create, Don't Edit !!!*/");
        classSource.AppendLine();
        //添加引用
        classSource.AppendLine("using UnityEngine;");
        classSource.AppendLine("using System.Collections.Generic;");
        classSource.AppendLine("using System;");
        classSource.AppendLine("using System.IO;");
        classSource.AppendLine();
        //生成CSharp数据类
        if(excelMediumData.isCreateCSharp)
        
            //生成行数据类,记录每行数据
            classSource.AppendLine(CreateExcelRowItemClass(itemClassName,excelMediumData));
            classSource.AppendLine();
            //生成整体数据类,记录整个Excel的所有行数据
            classSource.AppendLine(CreateExcelAllDataClass(dataClassName,itemClassName,excelMediumData));
            classSource.AppendLine();
        
        //生成Asset创建类
        if(excelMediumData.isCreateAssignment)
        
            //生成Asset操作类,用于自动创建Excel对应的Asset文件并赋值
            classSource.AppendLine(CreateExcelAssetClass(excelMediumData));
            classSource.AppendLine();
        
        //
        return classSource.ToString();
    

    //----------

    //生成行数据类
    private static string CreateExcelRowItemClass(string itemClassName,ExcelMediumData excelMediumData)
    
        //生成Excel行数据类
        StringBuilder classSource = new StringBuilder();
        //类名
        classSource.AppendLine("[Serializable]");
        classSource.AppendLine($"public class itemClassName : ExcelItemBase");
        classSource.AppendLine("");
        //声明所有字段
        for(int i = 0; i < excelMediumData.propertyNameArray.Length; i++)
        
            //跳过注释字段
            if(excelMediumData.annotationColList.Contains(i))
                continue;

            //添加注释
            if(i < excelMediumData.propertyNodeArray.Length)
            
                string propertyNode = excelMediumData.propertyNodeArray[i];
                if(!string.IsNullOrEmpty(propertyNode))
                
                    classSource.AppendLine("\\t/// <summary>");
                    classSource.AppendLine($"\\t/// propertyNode");
                    classSource.AppendLine("\\t/// </summary>>");
                
            

            //声明行数据类的字段
            string propertyName = excelMediumData.propertyNameArray[i];
            string propertyType = excelMediumData.propertyTypeArray[i];
            string typeStr = GetPropertyType(propertyType);
            classSource.AppendLine($"\\tpublic typeStr propertyName;");
        
        classSource.AppendLine("");
        return classSource.ToString();
    

    //----------

    //生成整体数据类
    private static string CreateExcelAllDataClass(string dataClassName,string itemClassName,ExcelMediumData excelMediumData)
    
        StringBuilder classSource = new StringBuilder();
        //类名
        classSource.AppendLine($"public class dataClassName : ExcelDataBase<itemClassName>");
        classSource.AppendLine("");
        //声明字段,行数据类数组
        classSource.AppendLine($"\\tpublic itemClassName[] items;");
        classSource.AppendLine();
        //id字段类型
        string idTypeStr = GetPropertyType(excelMediumData.propertyTypeArray[0]);
        //声明字典
        classSource.AppendLine($"\\tpublic Dictionary<idTypeStr,itemClassName> itemDic = new Dictionary<idTypeStr,itemClassName>();");
        classSource.AppendLine();
        //字段初始化方法
        classSource.AppendLine("\\tpublic void Init()");
        classSource.AppendLine("\\t");
        classSource.AppendLine("\\t\\titemDic.Clear();");
        classSource.AppendLine("\\t\\tif(items != null && items.Length > 0)");
        classSource.AppendLine("\\t\\t");
        classSource.AppendLine("\\t\\t\\tfor(int i = 0; i < items.Length; i++)");
        classSource.AppendLine("\\t\\t\\t");
        classSource.AppendLine("\\t\\t\\t\\titemDic.Add(items[i].id, items[i]);");
        classSource.AppendLine("\\t\\t\\t");
        classSource.AppendLine("\\t\\t");
        classSource.AppendLine("\\t");
        classSource.AppendLine();
        //字典获取方法
        classSource.AppendLine($"\\tpublic itemClassName GetitemClassName(idTypeStr id)");
        classSource.AppendLine("\\t");
        classSource.AppendLine("\\t\\tif(itemDic.ContainsKey(id))");
        classSource.AppendLine("\\t\\t\\treturn itemDic[id];");
        classSource.AppendLine("\\t\\telse");
        classSource.AppendLine("\\t\\t\\treturn null;");
        classSource.AppendLine("\\t");

        //每个字段Get函数
        classSource.AppendLine("\\t#region --- Get Method ---");
        classSource.AppendLine();

        for(int i = 1; i < excelMediumData.propertyNameArray.Length; i++)
        
            if(excelMediumData.annotationColList.Contains(i))
                continue;
            string propertyName = excelMediumData.propertyNameArray[i];
            string propertyType = excelMediumData.propertyTypeArray[i];
            //每个字段Get函数
            classSource.AppendLine(CreateCodePropertyMethod(itemClassName,idTypeStr,propertyName,propertyType));
        
        classSource.AppendLine("\\t#endregion");
        classSource.AppendLine("");
        return classSource.ToString();
    

    //生成数据字段对应Get方法
    private static string CreateCodePropertyMethod(string itemClassName,string idTypeStr,string propertyName,string propertyType)
    
        StringBuilder methodBuilder = new StringBuilder();
        string itemNameStr = propertyName.FirstOrDefault().ToString().ToUpper() + propertyName.Substring(1);
        string itemTypeStr = GetPropertyType(propertyType);
        //字段Get函数
        methodBuilder.AppendLine($"\\tpublic itemTypeStr GetitemNameStr(idTypeStr id)");
        methodBuilder.AppendLine("\\t");
        methodBuilder.AppendLine($"\\t\\tvar item = GetitemClassName(id);");
        methodBuilder.AppendLine("\\t\\tif(item == null)");
        methodBuilder.AppendLine("\\t\\t\\treturn default;");
        methodBuilder.AppendLine($"\\t\\treturn item.propertyName;");
        methodBuilder.AppendLine("\\t");
        //如果是一维数组
        if(propertyType.Contains("[]"))
        
            //typeStr:int[]或IntArr[] ,返回值:int或IntArr
            //string itemTypeStr1d = GetPropertyType(propertyType.Replace("[]",""));
            string itemTypeStr1d = itemTypeStr.Replace("[]","");
            methodBuilder.AppendLine($"\\tpublic itemTypeStr1d GetitemNameStr(idTypeStr id, int index)");
            methodBuilder.AppendLine("\\t");
            methodBuilder.AppendLine($"\\t\\tvar item0 = GetitemClassName (id);");
            methodBuilder.AppendLine("\\t\\tif(item0 == null)");
            methodBuilder.AppendLine("\\t\\t\\treturn default;");
            methodBuilder.AppendLine($"\\t\\tvar item1 = item0.propertyName;");
            methodBuilder.AppendLine("\\t\\tif(item1 == null || index < 0 || index >= item1.Length)");
            methodBuilder.AppendLine("\\t\\t\\treturn default;");
            methodBuilder.AppendLine("\\t\\treturn item1[index];");
            methodBuilder.AppendLine("\\t");
        
        //如果是二维数组
        if(propertyType.Contains("[][]"))
        
            //propertyType:int[][], 返回值:int
            string itemTypeStr1d = GetPropertyType(propertyType.Replace("[][]",""));
            methodBuilder.AppendLine($"\\tpublic itemTypeStr1d GetitemNameStr(idTypeStr id, int index1, int index2)");
            methodBuilder.AppendLine("\\t");
            methodBuilder.AppendLine($"\\t\\tvar item0 = GetitemClassName(id);");
            methodBuilder.AppendLine("\\t\\tif(item0 == null)");
            methodBuilder.AppendLine("\\t\\t\\treturn default;");
            methodBuilder.AppendLine($"\\t\\tvar item1 = item0.propertyName;");
            methodBuilder.AppendLine("\\t\\tif(item1 == null || index1 < 0 || index1 >= item1.Length)");
            methodBuilder.AppendLine("\\t\\t\\treturn default;");
            methodBuilder.AppendLine("\\t\\tvar item2 = item1[index1];");
            methodBuilder.AppendLine("\\t\\tif(item2.array == null || index2 < 0 || index2 >= item2.array.Length)");
            methodBuilder.AppendLine("\\t\\t\\treturn default;");
            methodBuilder.AppendLine("\\t\\treturn item2.array[index2];");
            methodBuilder.AppendLine("\\t");
        
        //
        return methodBuilder.ToString();
    

    //----------

    //生成Asset创建类
    private static string CreateExcelAssetClass(ExcelMediumData excelMediumData)
    
        string itemClassName = excelMediumData.excelName + "ExcelItem";
        string dataClassName = excelMediumData.excelName + "ExcelData";
        string assignmentClassName = excelMediumData.excelName + "AssetAssignment";

        StringBuilder classSource = new StringBuilder();
        classSource.AppendLine("#if UNITY_EDITOR");
        //类名
        classSource.AppendLine($"public class assignmentClassName");
        classSource.AppendLine("");
        //方法名
        classSource.AppendLine("\\tpublic static bool CreateAsset(ExcelMediumData excelMediumData, string excelAssetPath)");
        //方法体,若有需要可加入try/catch
        classSource.AppendLine("\\t");
        classSource.AppendLine("\\t\\tvar allRowItemDicList = excelMediumData.GetAllRowItemDicList();");
        classSource.AppendLine("\\t\\tif(allRowItemDicList == null || allRowItemDicList.Count == 0)");
        classSource.AppendLine("\\t\\t\\treturn false;");
        classSource.AppendLine();
        classSource.AppendLine("\\t\\tint rowCount = allRowItemDicList.Count;");
        classSource.AppendLine($"\\t\\tdataClassName excelDataAsset = ScriptableObject.CreateInstance<dataClassName>();");
        classSource.AppendLine($"\\t\\texcelDataAsset.items = new itemClassName[rowCount];");
        classSource.AppendLine();
        classSource.AppendLine("\\t\\tfor(int i = 0; i < rowCount; i++)");
        classSource.AppendLine("\\t\\t");
        classSource.AppendLine("\\t\\t\\tvar itemRowDic = allRowItemDicList[i];");
        classSource.AppendLine($"\\t\\t\\texcelDataAsset.items[i] = new itemClassName();");

        for(int i = 0; i < excelMediumData.propertyNameArray.Length; i++)
        
            if(excelMediumData.annotationColList.Contains(i))
                continue;
            string propertyName = excelMediumData.propertyNameArray[i];
            string propertyType = excelMediumData.propertyTypeArray[i];
            classSource.Append($"\\t\\t\\texcelDataAsset.items[i].propertyName = ");
            classSource.Append(AssignmentCodeProperty(propertyName,propertyType));
            classSource.AppendLine(";");
        
        classSource.AppendLine("\\t\\t");
        classSource.AppendLine("\\t\\tif(!Directory.Exists(excelAssetPath))");
        classSource.AppendLine("\\t\\t\\tDirectory.CreateDirectory(excelAssetPath);");
        classSource.AppendLine($"\\t\\tstring fullPath = Path.Combine(excelAssetPath,typeof(dataClassName).Name) + \\".asset\\";");
        classSource.AppendLine("\\t\\tUnityEditor.AssetDatabase.DeleteAsset(fullPath);");
        classSource.AppendLine("\\t\\tUnityEditor.AssetDatabase.CreateAsset(excelDataAsset,fullPath);");
        classSource.AppendLine("\\t\\tUnityEditor.AssetDatabase.Refresh();");
        classSource.AppendLine("\\t\\treturn true;");
        classSource.AppendLine("\\t");
        //          
        classSource.AppendLine("");
        classSource.AppendLine("#endif");
        return classSource.ToString();
    

    //声明Asset操作类字段
    private static string AssignmentCodeProperty(string propertyName,string propertyType)
    
        string stringValue = $"itemRowDic[\\"propertyName\\"]";
        string typeStr = GetPropertyType(propertyType);
        switch(typeStr)
        
            //字段
            case "int":
                return "StringUtility.StringToInt(" + stringValue + ")";
            case "float":
                return "StringUtility.StringToFloat(" + stringValue + ")";
            case "bool":
                return "StringUtility.StringToBool(" + stringValue + ")";
            case "Vector2":
                return "StringUtility.StringToVector2(" + stringValue + ")";
            case "Vector3":
                return "StringUtility.StringToVector3(" + stringValue + ")";
            case "Vector2Int":
                return "StringUtility.StringToVector2Int(" + stringValue + ")";
            case "Vector3Int":
                return "StringUtility.StringToVector3Int(" + stringValue + ")";
            case "Color":
                return "StringUtility.StringToColor(" + stringValue + ")";
            case "Color32":
                return "StringUtility.StringToColor32(" + stringValue + ")";
            case "string":
                return stringValue;
            //一维
            case "int[]":
                return "StringUtility.StringToIntArray(" + stringValue + ")";
            case "float[]":
                return "StringUtility.StringToFloatArray(" + stringValue + ")";
            case "bool[]":
                return "StringUtility.StringToBoolArray(" + stringValue + ")";
            case "Vector2[]":
                return "StringUtility.StringToVector2Array(" + stringValue + ")";
            case "Vector3[]":
                return "StringUtility.StringToVector3Array(" + stringValue + ")";
            case "Vector2Int[]":
                return "StringUtility.StringToVector2IntArray(" + stringValue + ")";
            case "Vector3Int[]":
                return "StringUtility.StringToVector3IntArray(" + stringValue + ")";
            case "Color[]":
                return "StringUtility.StringToColorArray(" + stringValue + ")";
            case "Color32[]":
                return "StringUtility.StringToColor32Array(" + stringValue + ")";
            case "string[]":
                return "StringUtility.StringToStringArray(" + stringValue + ")";
            //二维
            case "IntArr[]":
                return "StringUtility.StringToIntArray2D(" + stringValue + ")";
            case "FloatArr[]":
                return "StringUtility.StringToFloatArray2D(" + stringValue + ")";
            case "BoolArr[]":
                return "StringUtility.StringToBoolArray2D(" + stringValue + ")";
            case "Vector2Arr[]":
                return "StringUtility.StringToVector2Array2D(" + stringValue + ")";
            case "Vector3Arr[]":
                return "StringUtility.StringToVector3Array2D(" + stringValue + ")";
            case "Vector2IntArr[]":
                return "StringUtility.StringToVector2IntArray2D(" + stringValue + ")";
            case "Vector3IntArr[]":
                return "StringUtility.StringToVector3IntArray2D(" + stringValue + ")";
            case "ColorArr[]":
                return "StringUtility.StringToColorArray2D(" + stringValue + ")";
            case "Color32Arr[]":
                return "StringUtility.StringToColor32Array2D(" + stringValue + ")";
            case "StringArr[]":
                return "StringUtility.StringToStringArray2D(" + stringValue + ")";
            default:
                //枚举
                if(propertyType.StartsWith("enum"))
                
                    string enumType = propertyType.Split('|').FirstOrDefault();
                    string enumName = propertyType.Split('|').LastOrDefault();
                    if(enumType == "enum")
                        return "StringUtility.StringToEnum<" + enumName + ">(" + stringValue + ")";
                    else if(enumType == "enum[]")
                        return "StringUtility.StringToEnumArray<" + enumName + ">(" + stringValue + ")";
                    else if(enumType == "enum[][]")
                        return "StringUtility.StringToEnumArray2D<" + enumName + ">(" + stringValue + ")";
                
                return stringValue;
        
    

    //判断字段类型
    private static string GetPropertyType(string propertyType)
    
        string lowerType = propertyType.ToLower();
        switch(lowerType)
        
            case "int":
                return "int";
            case "int[]":
                return "int[]";
            case "int[][]":
                return "IntArr[]";
            case "float":
                return "float";
            case "float[]":
                return "float[]";
            case "float[][]":
                return "FloatArr[]";
            case "bool":
                return "bool";
            case "bool[]":
                return "bool[]";
            case "bool[][]":
                return "BoolArr[]";
            case "string":
                return "string";
            case "string[]":
                return "string[]";
            case "string[][]":
                return "StringArr[]";

            case "vector2":
                return "Vector2";
            case "vector2[]":
                return "Vector2[]";
            case "vector2[][]":
                return "Vector2Arr[]";
            case "vector2int":
                return "Vector2Int";
            case "vector2int[]":
                return "Vector2Int[]";
            case "vector2int[][]":
                return "Vector2IntArr[]";

            case "vector3":
                return "Vector3";
            case "vector3[]":
                return "Vector3[]";
            case "vector3[][]":
                return "Vector3Arr[]";
            case "vector3int":
                return "Vector3Int";
            case "vector3int[]":
                return "Vector3Int[]";
            case "vector3int[][]":
                return "Vector3IntArr[]";

            case "color":
                return "Color";
            case "color[]":
                return "Color[]";
            case "color[][]":
                return "ColorArr[]";
            case "color32":
                return "Color32";
            case "color32[]":
                return "Color32[]";
            case "color32[][]":
                return "Color32Arr[]";

            default:
                if(propertyType.StartsWith("enum"))
                
                    string enumType = propertyType.Split('|').FirstOrDefault();
                    string enumName = propertyType.Split('|').LastOrDefault();
                    switch(enumType)
                    
                        case "enum":
                            return enumName;
                        case "enum[]":
                            return $"enumName[]";
                        case "enum[][]":
                            return $"EnumArr<enumName>[]";
                        default:
                            break;
                    
                
                return "string";
        
    


 Excel数据中间类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//Excel中间数据
public class ExcelMediumData

    //Excel名字
    public string excelName;

    //Excel是否有效
    public bool isValid = false;
    //是否生成CSharp数据类
    public bool isCreateCSharp = false;
    //是否生成Asset创建类
    public bool isCreateAssignment = false;
    //是否生成Asset文件
    public bool isCreateAsset = false;

    //数据注释
    public string[] propertyNodeArray = null;
    //数据名称
    public string[] propertyNameArray = null;
    //数据类型
    public string[] propertyTypeArray = null;
    //List<每行数据内容>
    public List<string[]> allRowItemList = new List<string[]>();

    //注释行号
    public List<int> annotationRowList = new List<int>();
    //注释列号
    public List<int> annotationColList = new List<int>();

    //List<每行数据>,List<Dictionary<单元格字段名称, 单元格字段值>>
    public List<Dictionary<string,string>> GetAllRowItemDicList()
    
        if(propertyNameArray == null || propertyNameArray.Length == 0)
            return null;
        if(allRowItemList.Count == 0)
            return null;

        List<Dictionary<string,string>> allRowItemDicList = new List<Dictionary<string,string>>(allRowItemList.Count);

        for(int i = 0; i < allRowItemList.Count; i++)
        
            string[] rowArray = allRowItemList[i];
            //跳过空数据
            if(rowArray == null || rowArray.Length == 0)
                continue;
            //跳过注释数据
            if(annotationRowList.Contains(i))
                continue;

            //每行数据,对应字段名称和字段值
            Dictionary<string,string> rowDic = new Dictionary<string,string>();
            for(int j = 0; j < propertyNameArray.Length; j++)
            
                //跳过注释字段
                if(annotationColList.Contains(j))
                    continue;

                string propertyName = propertyNameArray[j];
                string propertyValue = j < rowArray.Length ? rowArray[j] : null;
                rowDic[propertyName] = propertyValue;
            
            allRowItemDicList.Add(rowDic);
        
        return allRowItemDicList;
    


Excel数据基类、扩展类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System;

public class ExcelDataBase<T> : ScriptableObject where T : ExcelItemBase




public class ExcelItemBase





[Serializable]
public struct StringArr

    public string[] array;

[Serializable]
public struct IntArr

    public int[] array;

[Serializable]
public struct FloatArr

    public float[] array;

[Serializable]
public struct BoolArr

    public bool[] array;


[Serializable]
public struct Vector2Arr

    public Vector2[] array;

[Serializable]
public struct Vector3Arr

    public Vector3[] array;

[Serializable]
public struct Vector2IntArr

    public Vector2Int[] array;

[Serializable]
public struct Vector3IntArr

    public Vector3Int[] array;

[Serializable]
public struct ColorArr

    public Color[] array;

[Serializable]
public struct Color32Arr

    public Color32[] array;


不支持泛型枚举序列化
//[Serializable]
//public struct EnumArr<T> where T : Enum
//
//    public T[] array;
//

字符串工具类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text.RegularExpressions;
using System;
using System.Text;
using System.Linq;
using System.Runtime.CompilerServices;

public static class StringUtility


    #region --- AddColor ---

    public static string AddColor(object obj,Color color)
    
        return AddColor(obj,color);
    
    public static string AddColor(this string str,Color color)
    
        //把颜色转换为16进制字符串,添加到富文本
        return string.Format("<color=#0>1</color>",ColorUtility.TohtmlStringRGBA(color),str);
    
    public static string AddColor(string str1,string str2,Color color)
    
        return AddColor(str1 + str2,color);
    
    public static string AddColor(string str1,string str2,string str3,Color color)
    
        return AddColor(str1 + str2 + str3,color);
    

    #endregion

    #region --- string length ---

    /// <summary>
    /// 化简字符串长度
    /// </summary>
    /// <param name="targetStr"></param>
    /// <param name="targetLength">目标长度,英文字符==1,中文字符==2</param>
    /// <returns></returns>
    public static string AbbrevStringWithinLength(string targetStr,int targetLength,string abbrevPostfix)
    
        //C#实际统计:一个中文字符长度==1,英文字符长度==1
        //UI显示要求:一个中文字符长度==2,英文字符长度==1

        //校验参数
        if(string.IsNullOrEmpty(targetStr) || targetLength <= 0)
            return targetStr;
        //字符串长度 * 2 <= 目标长度,即使是全中文也在长度范围内
        if(targetStr.Length * 2 <= targetLength)
            return targetStr;
        //遍历字符
        char[] chars = targetStr.ToCharArray();
        int curLen = 0;
        for(int i = 0; i < chars.Length; i++)
        
            //累加字符串长度
            if(chars[i] >= 0x4e00 && chars[i] <= 0x9fbb)
                curLen += 2;
            else
                curLen += 1;

            //如果当前位置累计长度超过目标长度,取0~i-1,即Substring(0,i)
            if(curLen > targetLength)
                return targetStr.Substring(0,i) + abbrevPostfix;
        
        return targetStr;
    

    #endregion

    #region --- String To Array ---

    //string

    public static byte StringToByte(string valueStr)
    
        byte value;
        if(byte.TryParse(valueStr,out value))
            return value;
        else
            return 0;
    

    public static string[] StringToStringArray(string valueStr,char splitSign = '|')
    
        if(string.IsNullOrEmpty(valueStr))
            return null;
        return valueStr.Split(splitSign);
    

    public static StringArr[] StringToStringArray2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
    
        if(string.IsNullOrEmpty(valueStr))
            return null;
        string[] strArr1 = valueStr.Split(splitSign1);
        if(strArr1.Length == 0)
            return null;

        StringArr[] arrArr = new StringArr[strArr1.Length];
        for(int i = 0; i < strArr1.Length; i++)
        
            arrArr[i] = new StringArr()
            
                array = strArr1[i].Split(splitSign2)
            ;

        
        return arrArr;
    

    //int

    public static int StringToInt(string valueStr)
    
        int value;
        if(int.TryParse(valueStr,out value))
            return value;
        else
            return 0;
    

    public static int[] StringToIntArray(string valueStr,char splitSign = '|')
    
        if(string.IsNullOrEmpty(valueStr))
            return null;

        string[] valueArr = valueStr.Split(splitSign);
        if(valueArr == null || valueArr.Length == 0)
            return null;

        int[] intArr = new int[valueArr.Length];
        for(int i = 0; i < valueArr.Length; i++)
        
            intArr[i] = StringToInt(valueArr[i]);
        
        return intArr;
    

    public static IntArr[] StringToIntArray2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
    
        if(string.IsNullOrEmpty(valueStr))
            return null;
        string[] strArr1 = valueStr.Split(splitSign1);
        if(strArr1.Length == 0)
            return null;

        IntArr[] arrArr = new IntArr[strArr1.Length];
        for(int i = 0; i < strArr1.Length; i++)
        
            arrArr[i] = new IntArr()
            
                array = StringToIntArray(strArr1[i],splitSign2)
            ;

        
        return arrArr;
    

    //float

    public static float StringToFloat(string valueStr)
    
        float value;
        if(float.TryParse(valueStr,out value))
            return value;
        else
            return 0;
    

    public static float[] StringToFloatArray(string valueStr,char splitSign = '|')
    
        if(string.IsNullOrEmpty(valueStr))
            return null;

        string[] valueArr = valueStr.Split(splitSign);
        if(valueArr == null || valueArr.Length == 0)
            return null;

        float[] floatArr = new float[valueArr.Length];
        for(int i = 0; i < valueArr.Length; i++)
        
            floatArr[i] = StringToFloat(valueArr[i]);
        
        return floatArr;
    

    public static FloatArr[] StringToFloatArray2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
    
        if(string.IsNullOrEmpty(valueStr))
            return null;
        string[] strArr1 = valueStr.Split(

Unity中基于EPPLUS的Excel转换以及Json数据读取

摘要:

主要使用Epplus的的表格转换功能,将表格转换成Json,而Unity中Json的使用要搭配对应结构的类,故顺便将利用表格结构生成对应的类,免除人工创建的麻烦过程。

示例:

表格示例:

编辑模式下:
生成Json:

生成Class:

运行时数据读取:

代码

Json数据读取器以及泛型基类
using UnityEngine;
public class JsonDataManager : Singleton<JsonDataManager>

    public JsonDataList<T> LoadData<T>()
    
        string json = Resources.Load<TextAsset>("Json/" + typeof(T).Name).text;
        return JsonUtility.FromJson<JsonDataList<T>>(json);
    

using System.Collections.Generic;
using System;

[Serializable]
public class JsonDataList<T>

    public List<T> datas = new List<T>();


Excel转换类
using UnityEngine;
using UnityEditor;
using System.IO;
using OfficeOpenXml;
using System.Collections.Generic;
using System;
using System.Text;
/// <summary>
/// 使用EPPlus获取表格数据,同时导出对应的Json以及Class.
/// </summary>
public class ExcelExporter

    /// <summary>
    /// Excel表格路径
    /// </summary>
    private const string excelPath = "../Assets/Excels";
    /// <summary>
    /// 导出的Json路径
    /// </summary>
    private const string configPath = "../Assets/Resources/Json";
    /// <summary>
    /// 导出的类路径
    /// </summary>
    private const string classPath = "../Assets/Scripts/Configs";

    /// <summary>
    /// 属性行
    /// </summary>
    private const int propertyIndex = 2;
    /// <summary>
    /// 类型行
    /// </summary>
    private const int typeIndex = 3;
    /// <summary>
    /// 值行
    /// </summary>
    private const int valueIndex = 4;



    [MenuItem("Tools/ExportExcel")]
    private static void ExportConfigs()
    
        try
        
            FileInfo[] files = Files.LoadFiles(excelPath);

            foreach (var file in files)
            
                //过滤文件
                if (file.Extension != ".xlsx") continue;
                ExcelPackage excelPackage = new ExcelPackage(file);
                ExcelWorksheets worksheets = excelPackage.Workbook.Worksheets;
                //只导表1
                ExcelWorksheet worksheet = worksheets[1];

                ExportJson(worksheet, Path.GetFileNameWithoutExtension(file.FullName));
                ExportClass(worksheet, Path.GetFileNameWithoutExtension(file.FullName));

            
            AssetDatabase.Refresh();
        
        catch (Exception e)
        
            Debug.LogError(e.ToString());
        
    

    /// <summary>
    /// 导出类
    /// </summary>
    private static void ExportClass(ExcelWorksheet worksheet, string fileName)
    
        string[] properties = GetProperties(worksheet);
        StringBuilder sb = new StringBuilder();
        sb.Append("using System;\\t\\n");
        sb.Append("[Serializable]\\t\\n");
        sb.Append($"public class fileNameConfig\\n");//类名
        sb.Append("\\n");

        for (int col = 1; col <= properties.Length; col++)
        
            string fieldType = GetType(worksheet, col);
            string fieldName = properties[col - 1];
            sb.Append($"\\tpublic fieldType fieldName;\\n");
        

        sb.Append("\\n\\n");
        Files.SaveFile(classPath, string.Format("0Config.cs", fileName), sb.ToString());

    
    /// <summary>
    /// 导出JSON
    /// </summary>
    private static void ExportJson(ExcelWorksheet worksheet, string fileName)
    
        string str = "";
        int num = 0;
        string[] properties = GetProperties(worksheet);
        for (int col = 1; col <= properties.Length; col++)
        
            string[] temp = GetValues(worksheet, col);
            num = temp.Length;
            foreach (var value in temp)
            
                str += GetJsonK_VFromKeyAndValues(properties[col - 1],
                    Convert(GetType(worksheet, col), value)) + \',\';
            
        
        //获取key:value的字符串
        str = str.Substring(0, str.Length - 1);
        str = GetJsonFromJsonK_V(str, num);
        str = GetUnityJsonFromJson(str);
        Files.SaveFile(configPath, string.Format("0Config.json", fileName), str);
    

    /// <summary>
    /// 获取属性
    /// </summary>
    private static string[] GetProperties(ExcelWorksheet worksheet)
    
        string[] properties = new string[worksheet.Dimension.End.Column];
        for (int col = 1; col <= worksheet.Dimension.End.Column; col++)
        
            if (worksheet.Cells[propertyIndex, col].Text == "")
                throw new System.Exception(string.Format("第0行第1列为空", propertyIndex, col));
            properties[col - 1] = worksheet.Cells[propertyIndex, col].Text;
        
        return properties;
    

    /// <summary>
    /// 获取值
    /// </summary>
    private static string[] GetValues(ExcelWorksheet worksheet, int col)
    
        //容量减去前三行
        string[] values = new string[worksheet.Dimension.End.Row - 3];
        for (int row = valueIndex; row <= worksheet.Dimension.End.Row; row++)
        
            values[row - valueIndex] = worksheet.Cells[row, col].Text;
        
        return values;
    

    /// <summary>
    /// 获取类型
    /// </summary>
    private static string GetType(ExcelWorksheet worksheet, int col)
    
        return worksheet.Cells[typeIndex, col].Text;
    

    /// <summary>
    /// 通过类型返回对应值
    /// </summary>
    private static string Convert(string type, string value)
    
        string res = "";
        switch (type)
        
            case "int": res = value; break;
            case "int32": res = value; break;
            case "int64": res = value; break;
            case "long": res = value; break;
            case "float": res = value; break;
            case "double": res = value; break;
            case "string": res = $"\\"value\\""; break;
            default:
                throw new Exception($"不支持此类型: type");
        
        return res;
    

    /// <summary>
    /// 返回key:value
    /// </summary>
    private static string GetJsonK_VFromKeyAndValues(string key, string value)
    
        return string.Format("\\"0\\":1", key, value);
    

    /// <summary>
    ///获取[key:value]转换为key:value,key:value,再变成[key:value,key:value,key:value,key:value]
    /// </summary>
    private static string GetJsonFromJsonK_V(string json, int valueNum)
    
        string str = "";
        string[] strs;
        List<string> listStr = new List<string>();
        strs = json.Split(\',\');
        listStr.Clear();
        for (int j = 0; j < valueNum; j++)
        
            listStr.Add("" + string.Format("0,1", strs[j], strs[j + valueNum]) + "");
        
        str = "[";
        foreach (var l in listStr)
        
            str += l + \',\';
        
        str = str.Substring(0, str.Length - 1);
        str += \']\';
        return str;
    

    /// <summary>
    /// 适应JsonUtility.FromJson函数的转换格式
    /// </summary>
    private static string GetUnityJsonFromJson(string json)
    
        return "" + "\\"datas\\":" + json + "";
    



文件读写的辅助类
using UnityEngine;
using System.IO;
using System.Collections.Generic;

public class Files

    public static FileInfo[] LoadFiles(string path)
    
        path = string.Format("0/1", Application.dataPath, path);

        if (Directory.Exists(path))
        
            DirectoryInfo directory = new DirectoryInfo(path);
            List<FileInfo> files = new List<FileInfo>();
            foreach (var file in directory.GetFiles("*"))
            
                if (file.Name.EndsWith(".meta"))
                    continue;
                if (file.Name.StartsWith("~")) continue;

                files.Add(file);
            
            return files.ToArray();
        
        else
        
            throw new System.Exception("路径不存在");
        
    

    public static void SaveFile(string path, string fileName, string fileContent)
    
        path = string.Format("0/1", Application.dataPath, path);

        if (Directory.Exists(path))
        
            path = string.Format("0/1", path, fileName);
            if (File.Exists(path))
            
                File.Delete(path);
            
            File.WriteAllText(path, fileContent);
        
        else
        
            throw new System.Exception("路径不存在");
        
    



以上是关于Unity升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件的主要内容,如果未能解决你的问题,请参考以下文章

Unity中基于EPPLUS的Excel转换以及Json数据读取

【Unity】读取Excel工具

如何创建 python+requests接口自动化测试框架

js解析json文件并生成对应的表格

有json数据如何填到excel里

Unity中解析Excel表格工具