Unity Excel转Json编辑器工具

news2025/1/3 16:27:58

功能说明:根据 .xlsx 文件生成对应的 JSON 文件,并自动创建脚本

注意事项

  1. Excel 读取依赖
    本功能依赖 EPPlus 库,只能读取 .xlsx 文件。请确保将该脚本放置在 Assets 目录下的 Editor 文件夹中。同时,在 Editor 下再创建一个 Excel 目录,并将你的 .xlsx 文件放到 Excel 目录下。注意:该目录下只能有一个 .xlsx 文件,且该文件是唯一的数据源。

    • Excel 文件格式要求
      • 第一行:字段名(与自动生成脚本中的字段对应)。
      • 第二行:中文注释。
      • 第三行:字段的数据类型(目前支持 intfloatdoublestringbool 和数组类型,如 int[]string[])。
      • 第四行开始:实际数据。
    • Epplus依赖获取查看我的另一篇文章Nuget For Unity插件介绍_nugetforunity-CSDN博客

 

  1. 生成脚本与 JSON 文件
    使用编辑器中的 ExcelTool 进行生成,点击 读取 Excel 按钮后,将自动执行以下操作:

    • 删除之前生成的脚本目录和 JSON 目录(如果存在),然后重新生成它们。
    • 注意:在这两个目录下请不要放置其他文件,因为此工具会在每次生成时覆盖这些目录。
  2. 关于数组格式
    数组数据需要按如下格式写入:
    例如1|2|3。数组成员使用 | 分隔。

  3. 支持多 sheet
    一个 .xlsx 文件中可以包含多个 sheet。在读取时,工具会读取所有 sheet 数据。请注意:

    • 将每个 sheet 的名字改为与要生成的脚本名一致。
    • Sheet 名称必须符合 C# 的命名规范,建议使用 TB_ 开头。

提示

  • 脚本命名:请确保每个 sheet 的名字与生成的 C# 脚本的名称一致。
  • 字段类型:当前支持的字段类型包括基本数据类型(intfloatdoublestringbool)以及数组类型(如 int[]string[])。
  • 感谢原作者:特别感谢原作者“小人”的贡献,我仅添加了一些功能,以下是他在 B站的教程视频地址:Unity中简单根据excel文件自动生成对应的C#脚本及json文件_哔哩哔哩_bilibili

格式

1f84954687904290b9d376aa79ce3845.png

保证这个目录格式

3afd64d1e4a9491189906f9d9d3850f6.png

Excel格式

 

源码

using OfficeOpenXml;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Text;
using System;
using UnityEditor;
using UnityEngine;

/*

功能:根据.xlsx文件生成对应的json文件,并自动创建脚本

注意:

   一 Excel读取依赖EPPlus,只能读.xlsx文件,Asset下创建一个Editor,同时将该脚本放到Editor下
      然后在Editor下再创建一个Excel目录,然后将你的Excel文件放到Excel目录下,注意Excel目录下
      只能有一个Excel文件,同时Excel格式要符合下列要求,最后Excel是唯一的数据源,所以除了保护好
      你的Excel文件外,其他的可以重新生成.使用编辑器上的ExcelTool/读取Excel按钮生成Json和脚本
      使用该按钮会删除脚本目录和json目录然后重新生成(如果已经生成过),所以不要在这两个目录下
      放置其他文件.

   二 xlsx第一行为英文字段与自动生成脚本中的字段对应

      第二行为中文注释

      第三行为字段的数据类型,目前支持int float double string bool 数组

      数组的写法统一为基本数据类型+[] 如int[] 、string[]

      第四行开始为实际数据

   三 xlsx文件中可以有很多sheet.读取时会读取xlsx的全部sheet

      要将对应的sheet的名字改为与要生成的脚本名一致,所以sheet名要符合C#的命名规范,建议使用TB_开头

   四 数组的书写格式形如:1|2|3,数组成员使用|分隔

   五 感谢原作者小人,我仅做了一些功能补充,下面的地址是小人的b站视频地址
    
*/
[HelpURL("https://www.bilibili.com/video/BV16f421Q7zA/?spm_id_from=333.1007.top_right_bar_window_default_collection.content.click&vd_source=f9b5906b25cd5ca40ec79f317993905b")]
public class ExcelTool
{

    //命名空间列表
    private static List<string> nameSpaceList = new List<string>()
        { "using System;", "[Serializable]"};
    //Root目录,包含Excel本身,Excel生成的json和脚本
    private static readonly string excel = Application.dataPath + "/Editor/Excel";
    //Excel文件
    private static string excelFilePath = excel;
    //脚本目录
    private static readonly string scriptsFolder = excel + "/ExcelScripts";
    //json目录
    private static string jsonFolder = excel + "/Json";

    [MenuItem("ExcelTool/读取Excel")]
    public static void TestTool()
    {


        // 获取目录下所有以 .xlsx 结尾的文件(不包括子目录中的文件)
        string[] files = Directory.GetFiles(excel, "*.xlsx");
        if (files.Length > 0)
        {
            // 获取第一个文件的完整路径
            string firstFilePath = files[0];

            // 获取文件名(不包括路径)
            string fileName = Path.GetFileName(firstFilePath);
            excelFilePath = excelFilePath + "/" + fileName;
        }
        if (!File.Exists(excelFilePath))
        {
            Debug.LogError("excel文件不存在");
            return;
        }

        CreateDirectory(scriptsFolder);
        CreateDirectory(jsonFolder);
        var res = ReadExcel(excelFilePath);
        for (int i = 0; i < res.Count; i++)
        {
            string path = scriptsFolder + "/" + res[i].scriptName + ".cs";
            CreateAScript(path, res[i].scriptName, nameSpaceList, res[i].fieldType, res[i].fieldName);
            CreateAJson(res[i].scriptName, res[i].fieldType, res[i].fieldName, res[i].dataDic);
        }



        AssetDatabase.Refresh();
    }

    private static void CreateDirectory(string path)
    {
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }
        else
        {
            path = ConvertToRelativePath(path);
            var b = AssetDatabase.DeleteAsset(path);
            Directory.CreateDirectory(path);
        }
    }
    // 将绝对路径转为相对路径
    private static string ConvertToRelativePath(string absolutePath)
    {
        // 获取项目的 'Assets' 文件夹路径
        string assetsPath = Application.dataPath;

        // 确保返回的路径是相对于 'Assets' 文件夹的
        if (absolutePath.StartsWith(assetsPath))
        {
            // 去掉 Application.dataPath 前缀,返回相对路径
            return "Assets" + absolutePath.Substring(assetsPath.Length);
        }
        Debug.LogError("路径不在 Assets 目录内: " + absolutePath);
        return absolutePath;
    }


    /// <summary>
    /// 读取 .xlsx文件,获取第一张sheet的内容
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    private static List<(string scriptName, List<string> fieldType, List<string> fieldName, Dictionary<int, List<string>> dataDic)>
       ReadExcel(string path)
    {
        int sheetsCount;
        var list = new List<(string scriptName, List<string> fieldType, List<string> fieldName, Dictionary<int, List<string>> dataDic)>();


        FileInfo fileInfo = new FileInfo(path);
        using (ExcelPackage excelPackage = new ExcelPackage(fileInfo))
        {
            sheetsCount = excelPackage.Workbook.Worksheets.Count;
        }

        for (int z = 0; z < sheetsCount; z++)
        {
            //生成的脚本名
            string scriptName;
            //字段类型列表
            List<string> fieldType = new List<string>();
            //字段名列表
            List<string> fieldName = new List<string>();
            //.xlsx除注释行之外的相关数据
            Dictionary<int, List<string>> dataDic = new Dictionary<int, List<string>>();

            using (ExcelPackage excelPackage = new ExcelPackage(fileInfo))
            {
                //取得.xlsx中的第一张sheet(EPPlus中下标从1或者0开始,取决于版本)
                ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[z];
                if (worksheet == null)
                {
                    sheetsCount++;
                    continue;
                }

                //取sheet的名字,即生成的脚本名
                scriptName = worksheet.Name;

                //取英文字段名
                //遍历表格第一行取字段名 注意索引下标
                for (int i = 1; i <= worksheet.Dimension.End.Column; i++)
                {
                    if (worksheet.Cells[1, i].Value is null)
                    {
                        Debug.LogError($"当前{worksheet}中第1行第{i}个单元格数据为空");
                        return null;
                    }
                    string field = worksheet.Cells[1, i].Value.ToString();
                    fieldName.Add(field);
                }

                //取字段类型
                //遍历第三行 同上
                for (int i = 1; i <= worksheet.Dimension.End.Column; i++)
                {
                    if (worksheet.Cells[3, i].Value is null)
                    {
                        Debug.LogError($"当前{worksheet}中第3行第{i}个单元格数据为空");
                        return null;
                    }
                    string field = worksheet.Cells[3, i].Value.ToString();
                    fieldType.Add(field);
                }

                //取实际数据

                for (int k = 4; k <= worksheet.Dimension.End.Row; k++)
                {
                    List<string> realData = new List<string>();
                    for (int j = 1; j <= worksheet.Dimension.End.Column; j++)
                    {
                        if (worksheet.Cells[k, j].Value is null)
                        {
                            Debug.LogError($"当前{worksheet}中第{k}行第{j}个单元格数据为空");
                            return null;
                        }

                        string data = worksheet.Cells[k, j].Value.ToString();
                        realData.Add(data);
                    }

                    dataDic[k] = realData;
                }
            }
            list.Add((scriptName, fieldType, fieldName, dataDic));

        }


        return list;
    }


    /// <summary>
    /// 判断字符串列表中是否包含重复成员,有,返回true
    /// </summary>
    /// <param name="list"></param>
    /// <returns></returns>
    private static bool HasRepeatedMember(List<string> list)
    {
        //往HaseSet中添加字符串,若不成功添加,说明有重复字符串 
        HashSet<string> hashSet = new HashSet<string>();
        for (int i = 0; i < list.Count; i++)
        {
            if (!hashSet.Add(list[i]))
            {
                return true;
            }
        }
        return false;
    }

    /// <summary>
    /// 创建一个C#脚本
    /// </summary>
    /// <param name="path">脚本保存路径</param>
    /// <param name="scriptName">脚本名</param>
    /// <param name="nameSpaceList">命名空间列表</param>
    /// <param name="fieldType">字段类型列表</param>
    /// <param name="fieldName">字段名列表</param>
    private static void CreateAScript(string path, string scriptName, List<string> nameSpaceList,
        List<string> fieldType, List<string> fieldName)
    {
        #region 安全校验
        if (fieldType is null || fieldType.Count == 0)
        {
            Debug.LogError($"{scriptName}字段类型列表错误");
            return;
        }
        if (fieldName is null || fieldName.Count == 0)
        {
            Debug.LogError($"{scriptName}字段名列表错误");
            return;
        }
        if (nameSpaceList is null || nameSpaceList.Count == 0)
        {
            Debug.LogError($"{scriptName}命名空间列表错误");
            return;
        }
        if (fieldType.Count != fieldName.Count)
        {
            Debug.LogError($"{scriptName}字段类型列表与字段名列表长度不一致");
            return;
        }
        //生成的字段以字母开头
        for (int i = 0; i < fieldName.Count; i++)
        {
            if (!Regex.IsMatch(fieldName[i], @"^[a-zA-Z_]"))
            {
                Debug.LogError($"{scriptName}中字段名应以字母开头");
                return;
            }
        }
        //避免生成字段重复
        if (HasRepeatedMember(fieldName))
        {
            Debug.LogError($"{scriptName}中出现重复字段");
            return;
        }
        #endregion

        using (StreamWriter writer = new StreamWriter(path))
        {
            //写入命名空间
            for (int i = 0; i < nameSpaceList.Count; i++)
            {
                writer.WriteLine(nameSpaceList[i]);
            }
            //写入脚本名
            writer.WriteLine($"public class {scriptName}");

            writer.WriteLine("{");

            //写入类型及字段
            for (int i = 0; i < fieldName.Count; i++)
            {
                writer.WriteLine($"public {fieldType[i]} {fieldName[i]} ;");
            }

            writer.WriteLine("}");
        }
    }

    /// <summary>
    /// 创建json文件
    /// </summary>
    /// <param name="jsonName">json文件名</param>;
    /// <param name="fieldType">字段类型列表</param>
    /// <param name="fieldName">字段名列表</param>
    /// <param name="dataDic">实际数据字典</param>
    private static void CreateAJson(string jsonName, List<string> fieldType, List<string> fieldName, Dictionary<int, List<string>> dataDic)
    {

        //写一行数据
        StringBuilder sb = new StringBuilder();
        sb.Append("[\n");


        for (int i = 4; i < dataDic.Count + 4; i++)
        {
            //写一行 追加
            string s = GetALine(fieldType, fieldName, dataDic[i]);
            sb.Append(s);
            //不是最后一项数据,加逗号
            if (i != dataDic.Count + 3)
            {
                sb.Append(",");
            }

            //换行
            sb.Append("\n");
        }

        //写最后一个括号
        sb.Append("]\n");

        string path = jsonFolder + "/" + jsonName + ".json";
        using (StreamWriter writer = new StreamWriter(path))
        {
            writer.WriteLine(sb.ToString());
        }
    }

    /// <summary>
    /// 将一行excel转为一行json
    /// </summary>
    /// <param name="fieldType">字段类型列表</param>
    /// <param name="fieldName">字段名列表</param>
    /// <param name="dataList">实际一行数据</param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private static string GetALine(List<string> fieldType, List<string> fieldName, List<string> dataList)
    {
        StringBuilder sb = new StringBuilder();
        //写括号
        sb.Append("{");
        //遍历列表 
        for (int i = 0; i < fieldType.Count; i++)
        {

            //写入主键
            string key = fieldName[i];
            sb.Append($"\"{key}\":");
            //写入值 
            string type = fieldType[i];
            string value = dataList[i];
            if (value is null)
            {
                throw new Exception("表格实际数据存在未配置项");
            }
            sb.Append($"{Convert(type, value)}");
            //写入逗号
            //不是最后一个就是逗号
            if (i != fieldType.Count - 1)
            {
                sb.Append(",");
            }
        }

        sb.Append("}");
        return sb.ToString();
    }

    //根据类型获取键所对应的值
    //如果不是数组 返回类型为 "Key":"Value" 中的value
    //如果是数组 返回类似于 ["1","2"] 的结构
    private static string Convert(string type, string value)
    {
        switch (type)
        {
            case "int":
            case "float":
            case "double":
            case "bool":
            case "string":
            case "long":
                //注此处返回的时候加了引号,避免格式错误
                return $"\"{value}\"";
            case "int[]":
            case "float[]":
            case "double[]":
            case "bool[]":
            case "string[]":
            case "long[]":

                return ArrayParse(value);
            default:
                throw new Exception("{type}类型暂未支持");
        }
    }

    /// <summary>
    /// 将数组转换成对应的字符串
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    private static string ArrayParse(string value)
    {
        //切分字符串得到数组
        var res = value.Split("|");
        StringBuilder sb = new StringBuilder();
        sb.Append("[");
        for (int i = 0; i < res.Length; i++)
        {
            sb.Append('"');
            sb.Append(res[i]);
            sb.Append('"');
            //不是数组最后一个加,
            if (i != res.Length - 1)
            {
                sb.Append(",");
            }
        }

        sb.Append("]");
        return sb.ToString();
    }

}

 

直接获取该项目

ExcelToJson: ExcelToJson

总结

该工具可以帮助你轻松地将 .xlsx 文件中的数据转换为 JSON 文件,并自动生成对应的 C# 脚本,简化了数据处理和代码生成的流程。在使用时,务必遵循 Excel 文件格式要求,确保生成的脚本和 JSON 文件符合预期。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2269197.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【竞技宝】CS2:HLTV2024职业选手排名TOP15-xantares

北京时间2024年12月30日&#xff0c;HLTV年度选手排名正在持续公布中&#xff0c;今日凌晨正式公布了今年的TOP15选手为EternalFire战队的xantares选手。 选手简介 xantares是一名来自于土耳其的CS职业选手&#xff0c;出生于1995年&#xff0c;今年已经29岁。早在2012年&…

智能商业分析 Quick BI

Quick BI 是阿里云提供的一款智能商业分析&#xff08;BI&#xff09;工具&#xff0c;旨在帮助企业快速获取业务洞察、优化决策过程、提升数据分析效率。通过强大的数据可视化和分析功能&#xff0c;Quick BI 能够帮助用户轻松连接多种数据源、创建多维度的报表和仪表盘&#…

uniapp - 小程序实现摄像头拍照 + 水印绘制 + 反转摄像头 + 拍之前显示时间+地点 + 图片上传到阿里云服务器

前言 uniapp&#xff0c;碰到新需求&#xff0c;反转摄像头&#xff0c;需要在打卡的时候对上传图片加上水印&#xff0c;拍照前就显示当前时间日期地点&#xff0c;拍摄后在呈现刚才拍摄的图加上水印&#xff0c;最好还需要将图片上传到阿里云。 声明 水印部分代码是借鉴的…

2024年12月31日Github流行趋势

项目名称&#xff1a;free-programming-books 项目地址url&#xff1a;https://github.com/EbookFoundation/free-programming-books项目语言&#xff1a;HTML历史star数&#xff1a;344575今日star数&#xff1a;432项目维护者&#xff1a;vhf, eshellman, davorpa, MHM5000, …

基于SpringBoot+Vue实现停车场管理系统

作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参与学生毕业答辩指导&#xff0c;…

Java学习路线:Servlet(一)认识和创建Servlet

目录 创建Servlet WebServlet Servlet的生命周期 认识和使用HttpServlet Servlet是JavaEE的一个标准&#xff0c;他就像JDBC一样&#xff0c;由官方定义了一系列接口&#xff0c;而具体的实现由我们自己编写&#xff0c;最后交给Web服务器如Tomcat来运行我们编写的Servlet…

公路边坡安全监测中智能化+定制化+全面守护的应用方案

面对公路边坡的安全挑战&#xff0c;我们如何精准施策&#xff0c;有效应对风险&#xff1f;特别是在强降雨等极端天气下&#xff0c;如何防范滑坡、崩塌、路面塌陷等灾害&#xff0c;确保行车安全&#xff1f;国信华源公路边坡安全监测解决方案&#xff0c;以智能化、定制化为…

机器人对物体重定向操作的发展简述

物体重定向操作的发展简述 前言1、手内重定向和外部重定向2、重定向原语3、重定向状态转换网络4、连续任意姿态的重定向5、利用其他环境约束重定向总结Reference 前言 对于一些特殊的任务&#xff08;如装配和打包&#xff09;&#xff0c;对物体放置的位姿由明确的要求&#…

【AndroidAPP】权限被拒绝:[android.permission.READ_EXTERNAL_STORAGE],USB设备访问权限系统报错

一、问题原因 1.安卓安全性变更 Android 12 的安全性变更&#xff0c;Google 引入了更严格的 PendingIntent 安全管理&#xff0c;强制要求开发者明确指定 PendingIntent 的可变性&#xff08;Mutable&#xff09;或不可变性&#xff08;Immutable&#xff09;。 但是&#xf…

windows系统安装完Anaconda之后怎么激活自己的虚拟环境并打开jupyter

1.在win主菜单中找到Anaconda安装文件夹并打开终端 文件夹内有所有安装后的Anaconda的应用软件和终端窗口启动窗口 点击Anaconda Prompt&#xff08;Anaconda&#xff09;就会打开类似cmd的命令终端窗口&#xff0c;默认打开的路径是用户名下的路径 2.激活虚拟环境 使用命令…

django33全栈班2025年004 录入数据

前言 通过前面的学习, 我们已经算是Python基本入门了. 如果你能熟练的掌握的话, 至少让你换台电脑, 在新电脑上搭建Python的开发环境肯定是没问题的. 我们呢也学习了第一行Python代码, 但是我们不知道这行代码是什么意思, 为什么能够运行, 怎么就能输出到控制台呢? 还有, …

Zeotero安装”translate for Zotero“插件

一、Zeotero6translate for Zotero 1.0.28 二、打开Zeotero官网&#xff0c;找到下面圈起来的 三、点击以上连接跳转&#xff0c;Releases windingwind/zotero-pdf-translate 下载 zotero-pdf-翻译.xpi 四、打开zeotero&#xff0c;工具>附加组件&#xff08;或插件&am…

郑州时空-TMS运输管理系统 GetDataBase 信息泄露漏洞复现

0x01 产品简介 郑州时空-TMS运输管理系统是一款专为物流运输企业设计的综合性管理软件,旨在提高运输效率、降低运输成本,并实现供应链的协同运作。系统基于现代计算机技术和物流管理方法,结合了郑州时空公司的专业经验和技术优势,为物流运输企业提供了一套高效、智能的运输…

小程序信息收集(小迪网络安全笔记~

免责声明&#xff1a;本文章仅用于交流学习&#xff0c;因文章内容而产生的任何违法&未授权行为&#xff0c;与文章作者无关&#xff01;&#xff01;&#xff01; 附&#xff1a;完整笔记目录~ ps&#xff1a;本人小白&#xff0c;笔记均在个人理解基础上整理&#xff0c;…

前端(九)js介绍(2)

js介绍(2) 文章目录 js介绍(2)一、函数1.1函数的两种形式1.2函数的作用域1.3声明与提升 二、bom操作三、dom操作 一、函数 1.1函数的两种形式 //有参函数 //js中的函数只能返回一个值&#xff0c;如果要返回多个需要放在数组或对象中 function func(a,b){return ab } func(1,…

国标GB28181-2022视频平台EasyGBS如何获取设备镜像ID?

在安防监控领域&#xff0c;随着技术的发展和标准的统一&#xff0c;国标GB28181-2022成为了视频监控系统互联互通的重要协议。EasyGBS作为一个遵循该国标的平台&#xff0c;为用户提供了强大的视频监控和管理功能。 在EasyGBS平台的使用过程中&#xff0c;设备镜像ID的获取是一…

【ADAS】高级驾驶辅助系统

自动驾驶入门—ADAS&#xff08;Advanced Driving Assistance System&#xff09;高级辅助驾驶系统 一、ADAS的官方介绍 二、信息辅助 1、行车监控类 2、危险预警类 3、驾驶便利类 三、控制辅助 1、紧急应对类 2、驾驶便利类 3、是车道保持类 4、智能灯光类 参考链接&#xff1…

Spring Boot + MinIO 实现分段、断点续传,让文件传输更高效

一、引言 在当今的互联网应用中&#xff0c;文件上传是一个常见的功能需求。然而&#xff0c;传统的文件上传方式在面对大文件或不稳定的网络环境时&#xff0c;可能会出现性能瓶颈和上传失败的问题。 传统文件上传&#xff0c;就像是用一辆小推车搬运大型家具&#xff0c;一…

搭建android开发环境 android studio

1、环境介绍 在进行安卓开发时&#xff0c;需要掌握java&#xff0c;需要安卓SDK&#xff0c;需要一款编辑器&#xff0c;还需要软件的测试环境&#xff08;真机或虚拟机&#xff09;。 早起开发安卓app&#xff0c;使用的是eclipse加安卓SDK&#xff0c;需要自行搭建。 目前开…

开发过程优化·自定义鼠标右键菜单

为了改善日常工作中的开发体验&#xff0c;我希望在某个项目目录下点击鼠标右键的快捷菜单&#xff0c;让程序自动为该项目引入一个内部的工具库文件并挂载到项目中。 实现该功能需要组装一些零碎的电脑应用知识&#xff0c;下面徐徐渐进依次说明&#xff1a; 1、在右键菜单中…