基础List的解析和拼接

添加list数据

我们首先给MonsterBase中添加list

1
2
3
4
5
6
7
[System.Serializable]
public class MonsterBase
{
//...
[XmlElement("AllString")]
public List<string> AllString { get; set; }
}

修改MonsterData中的Construction方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MonsterData : ExcelBase
{
/// <summary>
/// 编辑器下初始类转xml
/// </summary>
public override void Construction()
{
AllMonster = new List<MonsterBase>(5);
for (int i = 0; i < 5; i++)
{
MonsterBase monster = new MonsterBase
{
Id = i,
Name = i + "sq",
OutLook = "Assets/GameData/Prefabs/Attack",
Level = i,
Rarity = i,
Height = i + 1,
AllString = new List<string>() { "teststr","teststr2"}//添加List
};
AllMonster.Add(monster);
}
}
//...
}

选中Scripts——ExcelData——MonsterData.cs,然后右键——类转Xml

在GameData——Data——Xml中就有了新的MonsterData.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<MonsterData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AllMonster Id="0" Name="0sq" OutLook="Assets/GameData/Prefabs/Attack" Level="0" Rarity="0" Height="1">
<AllString>teststr</AllString>
<AllString>teststr2</AllString>
</AllMonster>
<AllMonster Id="1" Name="1sq" OutLook="Assets/GameData/Prefabs/Attack" Level="1" Rarity="1" Height="2">
<AllString>teststr</AllString>
<AllString>teststr2</AllString>
</AllMonster>
<AllMonster Id="2" Name="2sq" OutLook="Assets/GameData/Prefabs/Attack" Level="2" Rarity="2" Height="3">
<AllString>teststr</AllString>
<AllString>teststr2</AllString>
</AllMonster>
<AllMonster Id="3" Name="3sq" OutLook="Assets/GameData/Prefabs/Attack" Level="3" Rarity="3" Height="4">
<AllString>teststr</AllString>
<AllString>teststr2</AllString>
</AllMonster>
<AllMonster Id="4" Name="4sq" OutLook="Assets/GameData/Prefabs/Attack" Level="4" Rarity="4" Height="5">
<AllString>teststr</AllString>
<AllString>teststr2</AllString>
</AllMonster>
</MonsterData>

修改reg.xml文件

在游戏工程目录——Data——Reg中修改MonsterDataReg.xml,我们先把list的type定义为“listStr”,演示一下如何解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<data name="MonsterData" from="Monster.xlsx" to="MonsterData.xml">
<variable name="AllMonster" type="list">
<list name="MonsterBase" sheetname="怪物配置" mainKey="Id">
<variable name="Id" col="ID" type="int"></variable>
<variable name="Name" col="名字" type="string"></variable>
<variable name="OutLook" col="Prefab路径" type="string"></variable>
<variable name="Level" col="等级" type="int"></variable>
<variable name="Rarity" col="稀有度" type="int"></variable>
<variable name="Height" col="高度" type="float"></variable>
<variable name="AllString" col="测试list" type="listStr" split=";"></variable><!--在这里定义list-->
</list>
</variable>
</data>

解析list

在解析时,只需要在MapDataToSheet方法内(也就是将反序列化出来的data按照reg赋值给SheetData的阶段),按照对应的变量类型将list反射出来并按照指定的Split符号将list里面的元素合成在一起。

DataEditor里面添加GetSplitBaseList方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/// <summary>
/// 获取基础List按分隔符合成的字符串
/// </summary>
/// <param name="data">数据类</param>
/// <param name="varClass">变量信息</param>
/// <returns>合成的字符串</returns>
private static string GetSplitBaseList(object data, VarClass varClass)
{
string result = string.Empty;
if (string.IsNullOrEmpty(varClass.Split))
{
Debug.LogErrorFormat("{0} List的分隔符为空,请查看对应的Reg.xml", varClass.Name);
return result;
}
object dataList = GetMemberValue(data, varClass.Name);
int listCount = System.Convert.ToInt32(dataList.GetType().InvokeMember("get_Count",BindingFlags.Default | BindingFlags.InvokeMethod,null,dataList,new object[] { }));
for (int i = 0; i < listCount; i++)
{
string item = dataList.GetType().InvokeMember("get_Item",BindingFlags.Default|BindingFlags.InvokeMethod,null,dataList,new object[] {i}).ToString();
result = result + item;
if (i != listCount - 1)//最后一个不加Split符号
{
result = result + varClass.Split.Replace("\\n", "\n").Replace("\\r", "\r");//保留转义含义,直接在xml读取的内容只会被认为字符串,没有转义
}
}
return result;
}

修改DataEditor里面的MapDataToSheet方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private static void MapDataToSheet(object data, SheetClass rootSheet, Dictionary<string, SheetClass> sheetClassDic, Dictionary<string, SheetData> sheetDataDic)
{
//...
for (int i = 0; i < listCount; i++)
{
//item获取的是一行数据,我们根据Reg中存储的对应的变量名来获取具体数据
object item = dataList.GetType().InvokeMember("get_Item",BindingFlags.Default | BindingFlags.InvokeMethod,null,dataList,new object[] { i });

RowData rowData = new RowData();

childVars.ForEach(childVar =>
{
if(childVar.Type == "list")
{
//...
}
else if(childVar.Type == "listStr" || childVar.Type == "listFloat" || childVar.Type == "listInt" || childVar.Type == "listBool")//++++++++++++++++
{
string listCombine = GetSplitBaseList(item, childVar);
rowData.RowDataDic.Add(childVar.Col, listCombine);
}
else//如果此节点不是list
{
//...
}
});
//...
}
}

可以看到我们定义了许多新的type:listStrlistFloatlistIntlistBool,这些就是用来指定在Excel里合并的list类型

我们在Unity的GameData——Data——Xml——MonsterData.xml上右键——Xml转Excel,这时我们的“测试List”都会在同一个Excel里面拼接起来

自定义类List的解析和拼接

使用这种方法解析自定义类的List,必须保证自定义类的内容足够简单。自定义类不能再次嵌套List了。

添加list数据

我们首先给MonsterBase中添加list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[System.Serializable]
public class MonsterBase
{
//...
[XmlElement("AllString")]
public List<string> AllString { get; set; }
[XmlElement("AllBuff")]
public List<BuffBase> AllBuff { get; set; }
}
[System.Serializable]
public class BuffBase
{
[XmlAttribute("Id")]
public int Id { get; set; }
[XmlAttribute("Name")]
public string Name { get; set; }
}

修改Construction方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class MonsterData : ExcelBase
{
/// <summary>
/// 编辑器下初始类转xml
/// </summary>
public override void Construction()
{
AllMonster = new List<MonsterBase>(5);
for (int i = 0; i < 5; i++)
{
MonsterBase monster = new MonsterBase
{
Id = i,
Name = i + "sq",
OutLook = "Assets/GameData/Prefabs/Attack",
Level = i,
Rarity = i,
Height = i + 1,
AllString = new List<string>() { "teststr","teststr2"}//添加List
AllBuff = new List<BuffBase>
{
new BuffBase() { Id = i,Name = i + "Buff"} ,
new BuffBase() { Id = i + 1,Name = i + "Buff2"},
new BuffBase() { Id = i + 2,Name = i + "Buff3"}
}//添加自定义类List
};
AllMonster.Add(monster);
}
}
//...
}

选中Scripts——ExcelData——MonsterData.cs,然后右键——类转Xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="utf-8"?>
<MonsterData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AllMonster Id="0" Name="0sq" OutLook="Assets/GameData/Prefabs/Attack" Level="0" Rarity="0" Height="1">
<AllString>teststr</AllString>
<AllString>teststr2</AllString>
<AllBuff Id="0" Name="0Buff" />
<AllBuff Id="1" Name="0Buff2" />
<AllBuff Id="2" Name="0Buff3" />
</AllMonster>
<AllMonster Id="1" Name="1sq" OutLook="Assets/GameData/Prefabs/Attack" Level="1" Rarity="1" Height="2">
<AllString>teststr</AllString>
<AllString>teststr2</AllString>
<AllBuff Id="1" Name="1Buff" />
<AllBuff Id="2" Name="1Buff2" />
<AllBuff Id="3" Name="1Buff3" />
</AllMonster>
<AllMonster Id="2" Name="2sq" OutLook="Assets/GameData/Prefabs/Attack" Level="2" Rarity="2" Height="3">
<AllString>teststr</AllString>
<AllString>teststr2</AllString>
<AllBuff Id="2" Name="2Buff" />
<AllBuff Id="3" Name="2Buff2" />
<AllBuff Id="4" Name="2Buff3" />
</AllMonster>
<AllMonster Id="3" Name="3sq" OutLook="Assets/GameData/Prefabs/Attack" Level="3" Rarity="3" Height="4">
<AllString>teststr</AllString>
<AllString>teststr2</AllString>
<AllBuff Id="3" Name="3Buff" />
<AllBuff Id="4" Name="3Buff2" />
<AllBuff Id="5" Name="3Buff3" />
</AllMonster>
<AllMonster Id="4" Name="4sq" OutLook="Assets/GameData/Prefabs/Attack" Level="4" Rarity="4" Height="5">
<AllString>teststr</AllString>
<AllString>teststr2</AllString>
<AllBuff Id="4" Name="4Buff" />
<AllBuff Id="5" Name="4Buff2" />
<AllBuff Id="6" Name="4Buff3" />
</AllMonster>
</MonsterData>

修改Reg.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<data name="MonsterData" from="Monster.xlsx" to="MonsterData.xml">
<variable name="AllMonster" type="list">
<list name="MonsterBase" sheetname="怪物配置" mainKey="Id">
<variable name="Id" col="ID" type="int"></variable>
<variable name="Name" col="名字" type="string"></variable>
<variable name="OutLook" col="Prefab路径" type="string"></variable>
<variable name="Level" col="等级" type="int"></variable>
<variable name="Rarity" col="稀有度" type="int"></variable>
<variable name="Height" col="高度" type="float"></variable>
<variable name="AllString" col="测试list" type="listStr" split=";"></variable><!--在这里定义list-->
<variable name="AllBuff" type="list" col="测试BuffList" split="\n"><!--在这里定义自定义类List-->
<list name="BuffBase" sheetname="Buff配置" split=";"><!--必须要有SheetName,保证SheetClassDic和SheetDataDic的Key值-->
<variable name="Id" col="ID" type="int"></variable>
<variable name="Name" col="名字" type="string"></variable>
</list>
</variable>
</list>
</variable>
</data>

解析List

DataEditor里面添加GetSplitCustomList方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/// <summary>
/// 获取自定义类的List按分隔符合成的字符串,自定义类的数据比较少或者没办法确定父级结构
/// </summary>
/// <param name="data">数据类</param>
/// <param name="varClass">变量信息</param>
/// <param name="sheetClassDic"></param>
/// <returns>合成的字符串</returns>
private static string GetSplitCustomList(object data, VarClass varClass,SheetClass sheetClass)
{
string result = string.Empty;
if (string.IsNullOrEmpty(varClass.Split) || string.IsNullOrEmpty(sheetClass.Split))
{
Debug.LogErrorFormat("{0} List的分隔符为空,请查看对应的Reg.xml", varClass.Name);
return result;
}
object dataList = GetMemberValue(data, varClass.Name);
int listCount = System.Convert.ToInt32(dataList.GetType().InvokeMember("get_Count", BindingFlags.Default | BindingFlags.InvokeMethod, null, dataList, new object[] { }));
for (int i = 0; i < listCount; i++)
{
object item = dataList.GetType().InvokeMember("get_Item", BindingFlags.Default | BindingFlags.InvokeMethod, null, dataList, new object[] { i });
for (int j = 0; j < sheetClass.VarList.Count; j++)
{
object varItem = GetMemberValue(item, sheetClass.VarList[j].Name);
string varItemStr = varItem.ToString();
result += varItemStr;
if (j != sheetClass.VarList.Count - 1)
{
result += sheetClass.Split.Replace("\\n", "\n").Replace("\\r", "\r");//保留转义含义,直接在xml读取的内容只会被认为字符串,没有转义
}
}
if (i != listCount - 1)
{
result = result + varClass.Split.Replace("\\n","\n").Replace("\\r","\r");
}
}
return result;
}

使用这种方法解析自定义类的List,必须保证自定义类的内容足够简单。自定义类不能再次嵌套List了。

修改DataEditor里面的MapDataToSheet方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private static void MapDataToSheet(object data, SheetClass rootSheet, Dictionary<string, SheetClass> sheetClassDic, Dictionary<string, SheetData> sheetDataDic)
{
//...
for (int i = 0; i < listCount; i++)
{
//item获取的是一行数据,我们根据Reg中存储的对应的变量名来获取具体数据
object item = dataList.GetType().InvokeMember("get_Item",BindingFlags.Default | BindingFlags.InvokeMethod,null,dataList,new object[] { i });

RowData rowData = new RowData();

childVars.ForEach(childVar =>
{
if(childVar.Type == "list" && string.IsNullOrEmpty(childVar.Split)//+++
{
//...
}
else if(childVar.Type == "list")//++++
{
SheetClass tempSheetClass = sheetClassDic[childVar.ListSheetName];
string listCombine = GetSplitCustomList(item, childVar, tempSheetClass);
rowData.RowDataDic.Add(childVar.Col, listCombine);
}

else if(childVar.Type == "listStr" || childVar.Type == "listFloat" || childVar.Type == "listInt" || childVar.Type == "listBool")
{
//...
}
else//如果此节点不是list
{
//...
}
});
//...
}
}

我们在Unity的GameData——Data——Xml——MonsterData.xml上右键——Xml转Excel

自定义类List新建Sheet

如果自定义类比较杂内容比较多,就需要新开一个sheet,子sheet与主sheet通过Foreign指定的列名来联系。

修改Reg.xml文件

修改Reg.xml文件,我们使用上面的list的reg,在variable里面指定foreign,删除掉split和col

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<data name="MonsterData" from="Monster.xlsx" to="MonsterData.xml">
<variable name="AllMonster" type="list">
<list name="MonsterBase" sheetname="怪物配置" mainKey="Id">
<variable name="Id" col="ID" type="int"></variable>
<variable name="Name" col="名字" type="string"></variable>
<variable name="OutLook" col="Prefab路径" type="string"></variable>
<variable name="Level" col="等级" type="int"></variable>
<variable name="Rarity" col="稀有度" type="int"></variable>
<variable name="Height" col="高度" type="float"></variable>
<variable name="AllString" col="测试list" type="listStr" split=";"></variable><!--在这里定义list-->
<variable name="AllBuff" type="list" foreign="ID"><!--在这里定义自定义类List-->
<list name="BuffBase" sheetname="Buff配置"><!--必须要有SheetName,保证SheetClassDic和SheetDataDic的Key值-->
<variable name="Id" col="TestID" type="int"></variable><!--col不要和foreign指示的相同-->
<variable name="Name" col="名字" type="string"></variable>
</list>
</variable>
</list>
</variable>
</data>

解析List

我们想要的是:

创建子sheet时,子sheet第一列添加对应的主sheet的Key(Foreign)

子sheet新的列的内容通过上一级list结点(注意是list结点)的“mainKey”来反射获取。

修改DataEditor里面的MapDataToSheet方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
   private static void XmlToExcel(string xmlRegName)
{
//...
rootSheets.ForEach(sheet => { MapDataToSheet(data, sheet,allSheetClassDic,sheetDataDic,""); });//+++
//...
}


/// <summary>
/// 将反射出来的数据写进SheetData类字典里,这是一个递归方法
/// </summary>
/// <param name="data">从Xml反序列化出来的类数据</param>
/// <param name="rootSheet">Reg.Xml中每种list的枝干的SheetClass</param>
/// <param name="sheetClassDic">缓存的sheetClassDic</param>
/// <param name="sheetDataDic">需要输出的sheetDataDic</param>
private static void MapDataToSheet(object data, SheetClass rootSheet, Dictionary<string, SheetClass> sheetClassDic, Dictionary<string, SheetData> sheetDataDic,string mainKey)//+++
{
List<VarClass> childVars = rootSheet.VarList;
VarClass parentVar = rootSheet.ParentVar;

object dataList = GetMemberValue(data, parentVar.Name);//反射出数据类中对应的list变量
//反射出list的Count
int listCount =System.Convert.ToInt32(dataList.GetType().InvokeMember("get_Count", BindingFlags.Default | BindingFlags.InvokeMethod, null, dataList, new object[] { }));

SheetData sheetData = new SheetData();

if(!string.IsNullOrEmpty(parentVar.Foreign))//最外层主Sheet不会有,要从第二层开始思考此逻辑+++
{
sheetData.AllColName.Add(parentVar.Foreign);
sheetData.AllType.Add(parentVar.Type);
}
childVars.ForEach(item =>
{
if (!string.IsNullOrEmpty(item.Col))
{
sheetData.AllColName.Add(item.Col); //将SheetData的每列的表头加进去
sheetData.AllType.Add(item.Type);
}
});
string tempKey = mainKey;//先缓存mainKey,因为下面的 mainKey = GetMemberValue(item, rootSheet.MainKey).ToString();会修改mainKey+++
for (int i = 0; i < listCount; i++)
{
//item获取的是一行数据,我们根据Reg中存储的对应的变量名来获取具体数据
object item = dataList.GetType().InvokeMember("get_Item",BindingFlags.Default | BindingFlags.InvokeMethod,null,dataList,new object[] { i });

RowData rowData = new RowData();//每一行都新建一个RowData
if(!string.IsNullOrEmpty(parentVar.Foreign) && !string.IsNullOrEmpty(tempKey))
{
rowData.RowDataDic.Add(parentVar.Foreign, tempKey);//这一行要到第二层开始生效,要注意第二层之后List的所有数据对应的是相同的mainKey+++
}

if(!string.IsNullOrEmpty(rootSheet.MainKey))
{
mainKey = GetMemberValue(item, rootSheet.MainKey).ToString();//mainKey按照反射出来的值更新
}

childVars.ForEach(childVar =>
{
if(childVar.Type == "list" && string.IsNullOrEmpty(childVar.Split))//确定要新建子Sheet+++
{
SheetClass tempSheetClass = sheetClassDic[childVar.ListSheetName];
MapDataToSheet(item, tempSheetClass, sheetClassDic, sheetDataDic,mainKey);//递归获取所有dataList+++
}
else if(childVar.Type == "list")
{
SheetClass tempSheetClass = sheetClassDic[childVar.ListSheetName];
string listCombine = GetSplitCustomList(item, childVar, tempSheetClass);
rowData.RowDataDic.Add(childVar.Col, listCombine);
}
else if(childVar.Type == "listStr" || childVar.Type == "listFloat" || childVar.Type == "listInt" || childVar.Type == "listBool")
{
//...
}
else//如果此节点不是list
{
//...
}
});
//...
}
}

我们在Unity的GameData——Data——Xml——MonsterData.xml上右键——Xml转Excel