XML在开发中使用也很频繁,此时要以标签的形式来组织数据结构。C#提供了创建、解析、修改、查询等方法,可以很方便地操作它

创建XML

需要System.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
using UnityEngine;
using System.Xml;
using System.IO;

public class XMLCreate : MonoBehaviour
{
void Start()
{
//创建XmlDocument
XmlDocument xmlDoc = new XmlDocument();
XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null);
xmlDoc.AppendChild(xmlDeclaration);

//在Node中写入数据
XmlElement root = xmlDoc.CreateElement("XmlRoot");
xmlDoc.AppendChild(root);
XmlElement group = xmlDoc.CreateElement("Group");
group.SetAttribute("username", "ATAO2017");
group.SetAttribute("password", "123456");
root.AppendChild(group);

//读取Node并输出字符串
using (StringWriter stringWriter = new StringWriter())
{
using (XmlTextWriter xmlTextWriter = new XmlTextWriter(stringWriter))
{
xmlDoc.WriteTo(xmlTextWriter);
xmlTextWriter.Flush();//将缓存区的数据写入底层流中并刷新底层流(stringWriter)
Debug.Log(stringWriter.ToString());
}
}
}
}

XML数据

XML读取与修改

XML可以作为字符串来传递。我们可以动态读取XML字符串中的内容,并且修改它的内容,以重新生成新的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
using UnityEngine;
using System.Xml;
using System.IO;

public class XMLProcessing : MonoBehaviour
{
//xml字符串,供XmlDocument.LoadXml()方法使用。
string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><XmlRoot><Group username=\"ATAO2017\" password=\"123456\" /></XmlRoot>";

void Start()
{
//读取字符串xml
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);

//遍历节点
XmlNode nodes = xmlDoc.SelectSingleNode("XmlRoot");
foreach (XmlNode node in nodes.ChildNodes)
{
string username = node.Attributes["username"].Value;
string password = node.Attributes["password"].Value;
Debug.LogFormat($"username={username} password = {password}");
//修改其中一条数据
node.Attributes["password"].Value = "88888888";
}
//读取Node并输出字符串
using (StringWriter stringWriter = new StringWriter())
{
using (XmlTextWriter xmlTextWriter = new XmlTextWriter(stringWriter))
{
xmlDoc.WriteTo(xmlTextWriter);
xmlTextWriter.Flush();//将缓存区的数据写入底层流中并刷新底层流(stringWriter)
Debug.Log(stringWriter.ToString());
}
}
}
}

XML读取与修改

XML本地文件读写

xmlDocument类也提供了从文件中读取XML,或者将XML写入本地路径的方法。

新建XMLIOTest脚本

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
using UnityEngine;
using UnityEditor;
using System.Xml;
using System.IO;

public class XMLIOTest
{
[MenuItem("XML/WriteXml")]
static void XmlWrite()
{
string xmlPath = Path.Combine(Application.dataPath, "test.xml");
//如果XML文件已存在,就删除它
if (File.Exists(xmlPath))
File.Delete(xmlPath);

//创建XMLDocument
XmlDocument xmlDoc = new XmlDocument();
XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null);
xmlDoc.AppendChild(xmlDeclaration);

//在节点中写入数据
XmlElement root = xmlDoc.CreateElement("Root");
xmlDoc.AppendChild(root);

//循环写入3条数据
for (int i = 0; i < 3; i++)
{
XmlElement group = xmlDoc.CreateElement("Group");
group.SetAttribute("id", i.ToString());
group.SetAttribute("username", "ATAO2017");
group.SetAttribute("password", "123456");
root.AppendChild(group);
}

//写入文件
xmlDoc.Save(xmlPath);
AssetDatabase.Refresh();
}

[MenuItem("XML/LoadXml")]
static void XMLLoad()
{
string xmlPath = Path.Combine(Application.dataPath, "test.xml");

//如果文件存在则读取
if (File.Exists(xmlPath))
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlPath);
//遍历节点
XmlNode root = xmlDoc.SelectSingleNode("Root");
foreach (XmlNode group in root.ChildNodes)
{
string id = group.Attributes["id"].Value;
string username = group.Attributes["username"].Value;
string password = group.Attributes["password"].Value;
Debug.Log($"id = {id},username = {username},password = {password}");
}
}
}
}

创建的test.xml

debug读取

YAML

JSON的可读性差,XML的格式要求很严格

Unity采用了YAML格式来描述结构,它的预览性以及编辑性都非常好,数据与变量通过冒号来连接(必须带空格),例如,游戏中一些服务器列表的配置,或者调试性的开关,太不方便配置在表格中的数据,或者修改比较频繁的数据都可以使用YAML来配置,随时用随时改

YAML 入门教程 | 菜鸟教程 (runoob.com)

YamlDotNet

在Asset Store中免费添加YamlDotNet,支持PC和移动端,我们Download后,Import进入工程中

从Unity商店中免费

序列化和反序列化

YamlDotNet提供了运行时序列化和反序列化的方法。对于参与序列化的类中的变量,其属性必需设置成get和set,不然无法序列化

新建YAMLDaTaProcessing脚本

注意:不能在使用了Assembly Definition的文件夹下存放此脚本,即使在VS内添加了自定义Assembly对YamlDotNet的引用,Unity还是会报错

使用YamlDotNet序列化方法,可以不用把Class添加[System.Serializable]特性

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
using System.Collections.Generic;
using UnityEngine;
using YamlDotNet.Serialization;

public class YAMLDaTaProcessing : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
//创建对象
Data data = new Data();
data.name = "ATAO2017";
data.password = "123456";
data.list = new List<string>() { "A", "B", "C" };

//序列化YAML字符串
Serializer serializer = new Serializer();
string yaml = serializer.Serialize(data);
Debug.LogFormat("serializer : \n{0}", yaml);

//反序列化YAML字符串
Deserializer deserializer = new Deserializer();
Data data1 = deserializer.Deserialize<Data>(yaml);
Debug.LogFormat("deserializer : name={0} password={1}", data1.name, data1.password);
}


class Data
{
public string name { get; set; }
public string password;
public List<string> list { get; set; }
}
}

输出的Yaml的log

Serializable特性仅仅是一个标签,每一种序列化方式(不管是JSON、Xml还是Yaml)都有自己的一套方法,他们都按照自己的规则事件了ISerializable接口,有的在序列化之前需要确认Serializable特性,否则无法序列化;有的不需要查看Serializable特性,通过反射进行序列化。

读取配置

在游戏中,一些服务器列表或者一些临时调试的配置信息,可能需要频繁地添加、删除和修改。由于这些测试数据都是临时性的,就不太适合配置在Excel表格中。

我们将配置信息储存在yaml中,当包打出来后,直接修改配置文件就可以立即生效到游戏中。

我们在Unity的Assets文件目录下新建StreamingAssets文件夹,并新建yaml.txt,在其中输入

1
2
3
4
5
6
#服务器列表
ServerList : 192.168.1.1
#服务器端口
Port : 8080
#是否启动调试
Debug : true

在Unity中新建YAMLReadingConfig脚本

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
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using YamlDotNet.RepresentationModel;

public class YAMLReadingConfig : MonoBehaviour
{
//Yaml有自己的字典类序列化方法,所以这里只用到IDictionary接口。
private IDictionary<YamlNode, YamlNode> m_MappingData;
void Start()
{
//读取Yaml字符串
string document = File.ReadAllText(Path.Combine(Application.streamingAssetsPath, "yaml.txt"));
var input = new StringReader(document);
var yaml = new YamlStream();
yaml.Load(input);

//读取Root节点
var mapping = (YamlMappingNode)yaml.Documents[0].RootNode;
m_MappingData = mapping.Children;
}

private void OnGUI()
{
GUILayout.Label(string.Format("<size=50>服务器列表 :{0}</size>", m_MappingData["ServerList"]));
GUILayout.Label(string.Format("<size=50>服务器端口 :{0}</size>", m_MappingData["Port"]));
GUILayout.Label(string.Format("<size=50>是否调试 :{0}</size>", m_MappingData["Debug"]));
}

}

在Unity中运行后

Unity显示调试