unity的xml存档方式

xml的序列化

序列化

使用c#的xml序列化类,可以实现对一个对象的序列化和反序列化。

XmlSerializer xml = new XmlSerializer(typeof(Fight));
Fight fight = new Fight() { hp = 60, atk = 30 };
xml.Serialize(File.Open("D:\\666.xml", FileMode.Create), fight);

存在自定义类Fight

public class Fight
{
	public int hp;
	public int atk;
}

此操作会在D盘生成一个名叫666.xml的文件。
打开后内容应该类似于

<?xml version="1.0" encoding="utf-8"?>
<Fight xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<hp>60</hp>
	<atk>30</atk>
</Fight>

序列化会查看这个类的所有属性,依次将这些数据以文字形式保存。
例如string,int,bool,时间类等基础类型,都有从文字转化而来的方法。
但图像(比如unity中的精灵),则不行。

如果一种数据集,存在以int类型为参数的索引器,那么这个字段或属性也可以序列化。
例如数组,List集合。字典,队列,栈这种数据类型则不行。

反序列化

XmlSerializer xml = new XmlSerializer(typeof(Fight));

Fight fight = (Fight)xml.Deserialize(File.OpenRead("X:\\666.xml"));
Console.WriteLine(fight.hp);
Console.WriteLine(fight.atk);

Deserialize方法可以读取xml文件,并分析其中的文字,转换为对应的类型。最后手动赋值。
这个过程和你手动赋值一样,所以序列化和反序列化操作,只会针对public的字段或属性。
但数组,集合等除外,他们是引用类型,只要可以读取,不需要写入权限。
此外,他会调用这个类型的公共无参构造器,他也要求序列化的类必须有一个公共无参构造器。


构造一个xml树

如果需要序列化多个对象,并把他们装在同一个文件中,那就需要手动的把他们装在一起。

XElement element = new XElement("Save");
element.Add(new XElement("Atk", 60));
element.Add(new XElement("Def", 120));
element.Save("D:\\666.xml");

此操作新建了一个xml对象,并添加了一些内容。最后保存在D盘的666.xml文件中
打开后内容应该类似于

<?xml version="1.0" encoding="utf-8"?>
<Save>
  <Atk>60</Atk>
  <Def>120</Def>
</Save>

读取:

XElement element = XElement.Load("D:\\666.xml");
Console.WriteLine(element.Element("Atk").Value);
Console.WriteLine(element.Element("Def").Value);

用xml树保存多个序列化内容

将序列化内容添加至xml树中

我们只需要把这个“手动添加的内容”改成序列化出来的东西,即可保存多个对象了。
但是xml序列化没有生成字符串的方式,只能写入到流中。
幸运的是,c#有一个字符串流,可以伪装成流。并将写入的东西以字符串形式输出。

过程如下

XmlSerializer xml = new XmlSerializer(typeof(Fight));
Fight fight = new Fight() { hp = 315, atk = 90 };

StringWriter sw = new StringWriter();
xml.Serialize(sw, fight);
XElement xel = XElement.Parse(sw.ToString());

XElement element = new XElement("Save");
element.Add(xel);
element.Save("D:\\666.xml");

生成的xml文件

<?xml version="1.0" encoding="utf-8"?>
<Save>
  <Fight xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <hp>315</hp>
    <atk>90</atk>
  </Fight>
</Save>

从xml树中序列化需要的内容

xml的反序列化也不支持从字符串直接读取。我们使用类似的方式先声明一个伪装的流。

XmlSerializer xml = new XmlSerializer(typeof(Fight));
XElement element = XElement.Load("D:\\666.xml");

StringReader sr = new StringReader(element.Element("Fight").ToString());
Fight fight = (Fight)xml.Deserialize(sr);

示例:序列化一个字典

序列化对象,添加至xml树中

字典中有多个对象,下列代码演示如何手动构造一个xml树,并将所有字典中的序列化后的内容添加至xml树中。

Dictionary<string, Fight> dic = new Dictionary<string, Fight>();
dic.Add("史莱姆", new Fight() { atk = 1, hp = 10 });
dic.Add("小骷髅", new Fight() { atk = 3, hp = 6 });
dic.Add("爆虫", new Fight() { atk = 15, hp = 1 });
dic.Add("精英史莱姆", new Fight() { atk = 4, hp = 15 });

XmlSerializer xml_1 = new XmlSerializer(typeof(string));
XmlSerializer xml_2 = new XmlSerializer(typeof(Fight));

XElement element = new XElement("Save");
foreach (var item in dic)
{
	XElement xel = new XElement("KV");

	StringWriter sw1 = new StringWriter();
	xml_1.Serialize(sw1, item.Key);
	xel.Add(XElement.Parse(sw1.ToString()));

	StringWriter sw2 = new StringWriter();
	xml_2.Serialize(sw2, item.Value);
	xel.Add(XElement.Parse(sw2.ToString()));

	element.Add(xel);
}
element.Save("D:\\666.xml");

生成文件内容如下

<?xml version="1.0" encoding="utf-8"?>
<Save>
  <KV>
    <string>史莱姆</string>
    <Fight xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <hp>10</hp>
      <atk>1</atk>
    </Fight>
  </KV>
  <KV>
    <string>小骷髅</string>
    <Fight xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <hp>6</hp>
      <atk>3</atk>
    </Fight>
  </KV>
  <KV>
    <string>爆虫</string>
    <Fight xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <hp>1</hp>
      <atk>15</atk>
    </Fight>
  </KV>
  <KV>
    <string>精英史莱姆</string>
    <Fight xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <hp>15</hp>
      <atk>4</atk>
    </Fight>
  </KV>
</Save>

读取xml文件,并反序列化至对象

由于你是手动将他们序列化的,因此反序列化的过程也需要你自己写逻辑。

Dictionary<string, Fight> dic = new Dictionary<string, Fight>();
XmlSerializer xml_1 = new XmlSerializer(typeof(string));
XmlSerializer xml_2 = new XmlSerializer(typeof(Fight));
XElement element = XElement.Load("D:\\666.xml");
foreach (var item in element.Elements("KV"))
{
	StringReader sr1 = new StringReader(item.Element("string").ToString());
	string s = (string)xml_1.Deserialize(sr1);

	StringReader sr2 = new StringReader(item.Element("Fight").ToString());
	Fight f = (Fight)xml_2.Deserialize(sr2);

	dic.Add(s, f);
}
Console.WriteLine(dic["史莱姆"].atk);

附录

存档特性

定义类

/// <summary>
/// 需要保存的属性,仅适用于静态可读写的属性。不区分是否公开。
/// 注意数据类型需要是可以序列化的。例如字典等类型不可用。
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
public class SaveAttribute : Attribute
{
	#region 特性部分

	/// <summary>
	/// 序列化的顺序,某些数据极为复杂,不希望摆在开头位置时可调整。
	/// 可以对类使用,控制类的顺序。
	/// 如果没有该特性,视为值为0。
	/// </summary>
	public int Order = 0;

	/// <summary>
	/// 此属性的作用区间
	/// 单次游戏使用的数据使用<see cref="SaveScope.Game"/>
	/// <br/>全局数据(例如成就)使用<see cref="SaveScope.Option"/>
	/// <br/>默认为单次游戏数据
	/// </summary>
	public SaveScope Scope = SaveScope.Game;

	#endregion 特性部分

	#region 私有字段部分

	private PropertyInfo property;
	private XmlSerializer xmlSerializer;
	private string DeclaringFullName;

	#endregion 私有字段部分

	#region 公开方法部分

	public XElement Serializer()
	{
		StringWriter writer = new StringWriter();
		xmlSerializer.Serialize(writer, property.GetValue(null));
		StringReader read = new StringReader(writer.ToString());
		return new XElement("Property", new XAttribute("Name", property.Name), XElement.Load(read));
	}

	public void SetValue(XElement reader)
	{
		property.SetValue(null, xmlSerializer.Deserialize(XmlReader.Create(reader.FirstNode.CreateReader(), new XmlReaderSettings())));
	}

	#endregion 公开方法部分

	#region 静态私有部分

	static SaveAttribute()
	{
		var pro = Assembly.GetExecutingAssembly()
					.GetTypes()
					.SelectMany(t => t.GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic))
					.Select(p => (p.GetCustomAttribute<SaveAttribute>(), p))
					.Where(p => p.Item1 != null && p.Item2.CanWrite && p.Item2.CanRead)
					.ToArray();
		var dic = pro.Select(p => p.Item2.PropertyType).Distinct().ToDictionary(d => d, d => new XmlSerializer(d));
		properties = pro.Select(save =>
						{
							save.Item1.property = save.Item2;
							save.Item1.xmlSerializer = dic[save.Item2.PropertyType];
							save.Item1.DeclaringFullName = save.Item2.DeclaringType.FullName;
							return save.Item1;
						}).OrderBy(p => p.property.DeclaringType.GetCustomAttribute<SaveAttribute>()?.Order ?? 0)
						.ThenBy(p => p.Order)
						.ToLookup(p => p.DeclaringFullName);
	}

	private static ILookup<string, SaveAttribute> properties;

	#endregion 静态私有部分

	#region 静态公开部分

	/// <summary>
	/// 搜索带有<see cref="SaveAttribute"/>的静态属性序列化入指定路径
	/// </summary>
	/// <param name="path">文件保存路径</param>
	public static XElement Write(string path = null, SaveScope scope = SaveScope.Game)
	{
		var xel = new XElement("Save", properties.Select(item =>
				new XElement("Class", new XAttribute("FullName", item.Key), item
					.Where(info => (info.Scope & scope) == info.Scope).Select(info => info.Serializer())))
					.Where(ele => ele.Elements().Any()));
		if (path != null)
			xel.Save(path);
		return xel;
	}

	/// <summary>
	/// 解析序列化文件,反序列化至指定属性中
	/// </summary>
	/// <param name="path">文件保存路径</param>
	public static void Read(string path)
	{
		foreach (var (a, b) in XElement.Load(path).Elements("Class")
			.Join(properties, a => (string)a.Attribute("FullName"), b => b.Key, (a, b) => (a, b))
			.SelectMany(c => c.a.Elements("Property")
			.Join(c.b, a => a.Attribute("Name")?.Value, b => b.property.Name, (a, b) => (a, b))))
		{
			b.SetValue(a);
		}
	}

	#endregion 静态公开部分
}

public enum SaveScope
{
	Game = 1 << 0,
	Option = 1 << 1
}

使用:

public class Map
{
	[Save] public static string name { get; set; } = "妖精花园";
}

静态属性上添加这个特性(无所谓是否为公开),他们会被标记。
调用他的静态方法,将所有标记的属性的值全部序列化。

SaveAttribute.Write("D:\\666.xml");

读取时,使用反序列化方法:

SaveAttribute.Read("D:\\666.xml");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值