Unity 基於excel2json批處理讀取Excel表並反序列化


excel2json是一款將Excel表格文件快速生成json和C#數據類的高效插件,詳情了解如下:

https://neil3d.github.io/coding/excel2json.html

該插件有兩種模式,分別是命令行和圖像界面;為了更方便愉快的進行大規模轉換,可以寫兩個批處理文件來實現:

 

 Single文件執行單個選中Excel文件轉換,AutoAll則執行該路徑下所有xlsx文件;輸出文件夾的位置為output,如果該目錄下無output文件夾,則自動創建:

 

 Single.bat詳情如下:

 1 @SET OUTPUT_FOLDER=.\output
 2 @SET EXE=.\tool\excel2json.exe
 3 
 4 if not exist %OUTPUT_FOLDER% md %OUTPUT_FOLDER%
 5 
 6 @echo off
 7 set path=%1
 8 echo 源文件路徑%path%
 9 set name=%~n1
10 echo 源文件名%name%
11 set outputpath=%OUTPUT_FOLDER%\%name%
12 echo 輸出文件路徑%outputpath%
13 
14 @CALL %EXE% --excel %path% --json %outputpath%.json --header 3 --csharp %outputpath%.cs -a
15 
16 pause

前兩行分別為輸出目錄和.exe文件路徑,后面使用該變量作為參數時格式為[%變量名%];[.\]代表相對路徑

第四行,如果不存在該路徑文件夾則自動創建,注意如果沒有這一行也沒有對應參數所指示的路徑,這時並不會自動創建路徑而是會直接報錯

第七行,一個%表示參數輸入,得到當前選擇的首個文件路徑

第九行,得到當前選擇的首個文件文件名(不包含后綴)

類似的還有:

%~d1\   得到當前選擇的首個文件所在磁盤符

%~dp1  得到當前選擇的首個文件目錄(不包含文件名和文件后綴名)

%~nx1 得到當前選擇的首個文件文件名和后綴

這里主要是為了保持輸出文件名與選擇文件名一致,最終輸出路徑為設置的輸出路徑目錄+源文件名

第十四行,調用@CALL 執行對應路徑下的exe文件,根據excel2json提供的命令行設置啟動參數。

 

AutoAll.bat詳情如下:

 1 @SET EXCEL_FOLDER=.\
 2 @SET OUTPUT_FOLDER=.\output
 3 @SET EXE=.\tool\excel2json.exe
 4 
 5 @ECHO Converting excel files in folder %EXCEL_FOLDER% ...
 6 if not exist %OUTPUT_FOLDER% md %OUTPUT_FOLDER%
 7 
 8 for /f "delims=" %%i in ('dir /b /a-d /s %EXCEL_FOLDER%\*.xlsx') do (
 9     @echo   processing %%~nxi 
10     @CALL %EXE% --excel %EXCEL_FOLDER%\%%~nxi --json %OUTPUT_FOLDER%\%%~ni.json --header 3 --csharp %OUTPUT_FOLDER%\%%~ni.cs -a
11 )
12 pause

上面這個批處理文件在幫助頁面中有類似示例,利用for循環對路徑內的文件遍歷查詢和批量執行。具體說明如下:

dir /b /a-d /s  從指定路徑遍歷搜索文件,路徑即為當前文件夾下的所有.xlsx文件,也可以修改前面的excel所在文件夾參數配置其他位置

%%~nxi與%%~ni 與Single.bat中類似,只不過不是1(首個文件)而是循環體中的變量i(當前遍歷的文件),i對應數目索引的指定文件

需要注意的是,在cmd模式下的循環變量為單個%+循環標識符(即%i),但在批處理文件中需要兩個百分號才行(即%%i)

 

提供已經寫好批處理的文件包下載鏈接:

https://files.cnblogs.com/files/koshio0219/excel2json.zip

這里統一將Execl導出為了單張數組類型,方便在Unity中進一步反序列化,如果想導出字典,可自行修改,或利用圖形界面分別導出

 

之所以默認導出數組類型,因為Unity默認的JsonUtility解析字典類型幾乎是不可能,即使強行可以,那也是用的兩個List做對應關系,跟真正的字典類型導出的Json文件格式區別很大,如果直接解析出來就是個空文件。

當然了,如果只是用於數據保存和讀寫,先序列化后再反序列化回來的話,這樣是不會出任何問題的,用兩個List來做對應關系來序列化字典是完全可行的,這個可以詳細見后面的補充實驗;反過來如果專門利用外部文件來執行反序列化,就有很多地方需要額外注意,很容易就反序列化失敗。

查看excel2json工程的源代碼就可以知道,里邊用的Json序列化方式為Newtonsoft.Json,如果實在需要用字典來解析,可以直接導入Newtonsoft.Json到Unity中使用。

 

為了方便對比,下面分別進行數組Json與字典Json的反序列化測試:

1.數組型Json(或List型)

比如下面這段Json和C#文件:(通過excel2json導出)

 1  [
 2   {
 3     "ID": "4l523",
 4     "Hp": 5,
 5     "Atk": 6.3,
 6     "Def": 7,
 7     "State": ""
 8   },
 9   {
10     "ID": "p6",
11     "Hp": 7,
12     "Atk": 8,
13     "Def": 2.3,
14     "State": ""
15   },
16   {
17     "ID": 0.3,
18     "Hp": 0.2,
19     "Atk": "2.3,7",
20     "Def": 9,
21     "State": ""
22   }
23 ]
1 [System.Serializable]
2 public class Buff
3 {
4     public string ID; // 編號
5     public int Hp; // 血量
6     public float Atk; // 攻擊
7     public float Def; // 防御
8     public BuffData State; // 狀態
9 }

上面的Json中,因為腹黑的我在Excel表格中故意填錯了一些與當前類型不匹配的數據,導致出來的Json中的數據比較怪異,例如第三組中的ID,Hp,Atk,Def與當前的數據類型不符,且Atk一個表格中填了兩個數字;

當我們企圖直接利用JsonUtility反序列化該數組時,Unity會給你一個錯誤——JSON must represent an object type.:

 1 var data = JsonUtility.FromJson<Buff[]>(json.text); 

JsonUtility序列化數組時需要一個額外轉換,它必須包含一個默認的簽名類型才行,所以在Json文件外圍增加一個array的簽名即可成功反序列化:

 1 using UnityEngine;
 2 
 3 public class JsonHelper
 4 {
 5     public static T[] GetJsonArray<T>(string json)
 6     {
 7         string newJson = "{ \"array\": " + json + "}";
 8         Wrapper<T> wrapper = JsonUtility.FromJson<Wrapper<T>>(newJson);
 9         return wrapper.array;
10     }
11 
12     [System.Serializable]
13     private class Wrapper<T>
14     {
15         public T[] array;
16     }
17 }

需要注意的是,如果以該方式反序列化數組,之前導出的Json文件不能包含文件名,在上面的腳本中統一將文件名添加為array。而newJson的文件名稱必須與Wrapper類中的泛型數組T[]的變量名保持一致,才能保證反序列化出指定數據。

如果不利用泛型的話,則需要每個文件單獨寫一個類來進行反序列化,同樣的數組的變量名必須與Json中的Array名保持一致。

 

為了更方便的通過ID來讀取數據,也可以將得到的數組再遍歷一遍重新寫入一個字典中,通過反射在獲取ID的值作為鍵,但前提是規定每一個Json文件中必須有ID這一字段:

 1 public class JsonDatas<T>
 2 {
 3     public Dictionary<string, T> Dict = new Dictionary<string, T>();
 4     public static JsonDatas<T> FromJson(string json)
 5     {
 6         var re = new JsonDatas<T>();
 7         var datas = JsonHelper.GetJsonArray<T>(json);
 8         foreach(var data in datas)
 9         {
10             var info = data.GetType().GetField("ID");
11             var idstr = info.GetValue(data).ToString();
12             re.Dict.Add(idstr, data);
13         }
14         return re;
15     }
16 
17     public T Get(string ID)
18     {
19         return Dict[ID];
20     }
21 }

反射取字段值得時遇到了一個坑,特意記錄一下:

Type.GetField(string name) 這個是取字段的值,取不了屬性

Type.GetProperty(string name) 這個是取屬性的值,取不了字段

這兩個取出來的內容是不一樣的,請注意區分,不然半天也查不出錯誤出在哪里(說的就是我本人)

 

調試后的結果如下,能夠成功解析出Json了:

如此順利甚至讓人有點驚訝,之前瞎填的錯誤數據類型是怎么反序列化的呢?來看看第三組數據為什么沒有報錯 ,神奇的是,JsonUtility竟然自動幫你轉化為了對應的數據類型:

ID  0.3被轉為了“0.300000”;Hp 0.2 變為了0;更震驚的是,Atk竟然也沒有報錯,而是成功解析出了逗號前面的數字,emmm有點迷。

 

2.字典型Json

如果非要導出字典型Json來反序列化,那就不能再用Unity自帶的JsonUtility了,最好導入和序列化時用的是一樣的Newtonsoft.Json

這里是與Unity適配的Newtonsoft.Json包JsonNet.9.0.1.unitypackage下載地址:

 https://files.cnblogs.com/files/koshio0219/JsonNet.9.0.1.zip

如果是利用Newtonsoft.Json反序列化單個不帶任何簽名的字典,只用一句話就可以了,不需要建立任何新類:

 1 var data = JsonConvert.DeserializeObject<Dictionary<string, Buff>>(json.text); 

試比較帶簽名和不帶簽名的Json:

 1 {
 2   "Buff": {
 3     "4l523": {
 4       "ID": "4l523",
 5       "Hp": 5,
 6       "Atk": 6.3,
 7       "Def": 7,
 8       "State": ""
 9     },
10     "p6": {
11       "ID": "p6",
12       "Hp": 7,
13       "Atk": 8,
14       "Def": 2.3,
15       "State": ""
16     },
17     "0.3": {
18       "ID": 0.3,
19       "Hp": 2,
20       "Atk": 7,
21       "Def": 9,
22       "State": ""
23     }
24   }
25 }
View Code
 1 {
 2   "4l523": {
 3     "ID": "4l523",
 4     "Hp": 5,
 5     "Atk": 6.3,
 6     "Def": 7,
 7     "State": ""
 8   },
 9   "p6": {
10     "ID": "p6",
11     "Hp": 7,
12     "Atk": 8,
13     "Def": 2.3,
14     "State": ""
15   },
16   "0.3": {
17     "ID": 0.3,
18     "Hp": 2,
19     "Atk": 7,
20     "Def": 9,
21     "State": ""
22   }
23 }
View Code

只要帶有簽名或者存在多個表單文件在同一個Json中,就只能重新建立新類並解析該新類了,新類中的變量順序和變量名都必須與Json文件中的順序與簽名保持一致才能成功反序列化:

 

1     public class Buffs
2     {
3         //變量名稱Buff必須與Json中的簽名Buff一樣
4         public Dictionary<string, Buff> Buff = new Dictionary<string, Buff>();
5     }

叫人失落的是,Newtonsoft.Json並不會良心的幫你把錯誤的數據自動轉換,而是直接給你拋出一個錯誤,害的我只能手動修正錯誤數據,這一點和JsonUtility不同。

 

補充:

一個有趣的實驗——強行用Unity中的字典序列化方式來序列化Json文件會是怎樣?

開始之前,我們要明白的是,Unity默認情況下沒有給出任何字典序列化的方式,它只能蠢蠢的序列化List或者Array,但這並不能阻止我們,我們可以討巧的利用ISerializationCallbackReceiver接口來實現一個偽序列化,本質上還是用的兩個List:

 1 using UnityEngine;
 2 using System;
 3 using System.Collections.Generic;
 4 
 5 // Dictionary<TKey, TValue>
 6 [Serializable]
 7 public class Serialization<TKey, TValue> : ISerializationCallbackReceiver
 8 {
 9     [SerializeField]
10     List<TKey> keys;
11     [SerializeField]
12     List<TValue> values;
13 
14     Dictionary<TKey, TValue> target;
15     public Dictionary<TKey, TValue> ToDictionary() { return target; }
16 
17     public Serialization(Dictionary<TKey, TValue> target)
18     {
19         this.target = target;
20     }
21 
22     public void OnBeforeSerialize()
23     {
24         keys = new List<TKey>(target.Keys);
25         values = new List<TValue>(target.Values);
26     }
27 
28     public void OnAfterDeserialize()
29     {
30         var count = Math.Min(keys.Count, values.Count);
31         target = new Dictionary<TKey, TValue>(count);
32         for (var i = 0; i < count; ++i)
33         {
34             target.Add(keys[i], values[i]);
35         }
36     }
37 }

把之前反序列化出的數據再用該偽序列化方式來序列化為Json文件:

1         var SerializedBuff= new Serialization<string, Buff>(new Dictionary<string, Buff>());
2         var data = JsonConvert.DeserializeObject<Buffs>(json.text);
3         foreach(var item in data.Buff)
4         {
5             SerializedBuff.ToDictionary().Add(item.Key, item.Value);
6         }
7         var jsont = JsonUtility.ToJson(SerializedBuff);
8         Debug.Log(jsont);

jsont文本內容如下:

 1 {
 2     "keys":[
 3         "4l523",
 4         "p6",
 5         "0.3"],
 6     "values":[
 7         {
 8             "ID":"4l523",
 9             "Hp":5,
10             "Atk":6.300000190734863,
11             "Def":7.0,
12             "State":{
13             }
14         },
15         {
16             "ID":"p6",
17             "Hp":7,
18             "Atk":8.0,
19             "Def":2.299999952316284,
20             "State":{
21             }
22         },
23         {
24             "ID":"0.3",
25             "Hp":2,
26             "Atk":7.0,
27             "Def":9.0,
28             "State":{
29             }
30         }]
31 }

我們發現它確實和之前的Json文件結構大不相同,因為它根本不是字典而是兩個List做對應關系,實際上是Keys在一起Values在一起,只不過它們的索引是相互對應的。

當我們已經將Json文件轉化為了Unity可識別的形式后,就可以很容易的再進行反序列化了:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM