本文主要是介绍C#——XML序列化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
开发环境
VS2022
.net core 6.0
序列化概念
序列化是将内存中的对象或者对象图(一组相互引用的对象)拉平为一个可以保存或进行传输的字节流,或者XML节点。反序列化正好相反,它把数据流重新构造成内存中的一个对象或者对象图。
序列化用途
序列化和反序列化通常用于:
· 通过网络或程序边界传输对象
· 在文件或者数据库中保存对象
此外,它还可以用于深度克隆对象。而数据契约和XML序列化引擎也可以当作通用工具,用于加载和保存已知结构的XML文件。
可序列化的项
-
公共类的公共读/写属性和字段。
-
执行 ICollection 或 IEnumerable 的类 。
-
XmlElement 对象。
-
XmlNode 对象。
-
DataSet 对象。
序列化常用特性
XmlRoot——申明根结点,如: [XmlRoot(ElementName = "Root")]
XmlIgnore——忽悠某个属性或字段,如 :[XmlIgnore] public int Id;
XmlInclude——是否包含某个类,如:[XmlInclude(typeof(Results))]
XmlArray——申明集合
XmlArrayItem——申明集合中元素,如下:
[XmlArray("Targets", Namespace = "TwoPoint")][XmlArrayItem("Target", Namespace = "TwoPoint")]public Point[] Points { get; set; }
XML 序列化注意事项
使用 XmlSerializer 类时,应注意以下事项:
-
序列化数据只包含数据本身和类的结构。
-
只能序列化公共属性和字段。 属性必须具有公共访问器(get 和 set 方法)。
-
类必须具有无参数构造函数才能被 XmlSerializer 序列化。
-
方法不能被序列化。
-
如下所述,如果实现 IEnumerable 或 ICollection 的类满足某些要求,XmlSerializer 则可以处理这些类 。
实现 IEnumerable 的类必须实现采用单个参数的公共 Add 方法 。
实现 ICollection 的类(如 CollectionBase)必须具有采用整型的公共“Item”索引属性(在 C# 中为索引器),而且它必须有一个“integer”类型的公共“Count”属性 。 传递给 Add 方法的参数必须与从“Item”属性返回的类型相同,或者为此类型的基之一 。
常见错误及解决办法
以下为愚初次使用时遇到的一引问题,及解决办法:
不同命名空间下的相同类名下异常
InvalidOperationException: Types 'WpfApp.Xml.Results.Target' and 'WpfApp.Xml.PlanInfo.Target' both use the XML type name, 'Target', from namespace ''. Use XML attributes to specify a unique XML name and/or namespace for the type.
解决办法:如上黄色字体,即为序列化的对象添加Namespace,如下
[XmlType(Namespace = "WpfApp.Xml.Results")]public class Target{/// <summary>/// 编号/// </summary>[XmlAttributeAttribute(AttributeName = "id")]public double NO { get; set; }}
注意仅需要添加Namespace即可,不需要添加TypeName。
很多文章提到应该添加TypeName和Namespace,如下的操作方式:
[XmlType(TypeName = "PTarget", Namespace = "WpfApp.Xml.Info")]public class Target{/// <summary>/// 编号/// </summary>[XmlAttributeAttribute(AttributeName = "id")]public double NO { get; set; }}
但若添加了TypeName会导致你后一个不同NameSpace下的类改为你Namespace中设置的名称。
非集合属性或字段添加了集合特性
InvalidOperationException: For non-array types, you may use the following attributes: XmlAttribute, XmlText, XmlElement, or XmlAnyElement.
集合属性或字段添加了 XmlElement
集合不能使用它,否则会导致它的子元素全部显示为Targets,而应该如下:
[XmlArray("Targets")]
[XmlArrayItem("Target")]
public Point[] Points { get; set; }
XmlArray指定了集合Points在Xml中的显示名称,
XmlArrayItem指定了集合中元素在Xml中的显示名称。
对象序列化与反序列化常用代码
对象序列化为XML文件
/// <summary>/// 序列化对象为XML文件/// </summary>internal static void XmlSerializer<T>(string fileName, T t) where T : new(){try{XmlSerializer serializer = new(typeof(SerializationWrapper));using TextWriter tr = new StreamWriter(fileName, false, Encoding.UTF8);// 序列化包装类serializer.Serialize(tr, t);tr.Close();tr.Dispose();}catch (Exception ex){Debug.WriteLine("序列化失败");Exception exception = new("序列化失败");throw exception;}}
xml文件反序化为对象
/// <summary>/// 反序列化XML文件为对象/// </summary>/// <param name="fileName">文件名</paraminternal static T XmlDeserialize<T>(string fileName) where T : new() {using FileStream s = File.OpenRead(fileName);// 创建包装类的实例并添加对象XmlSerializer xs = new(typeof(T));T sw = (T)xs.Deserialize(s);return sw;}
上述代码最好还是添加上try……catch……以进行错误处理。
对象序列化为字符串
/// <summary>/// 将对象序列化为XML字符串/// </summary>/// <typeparam name="T">需要序列化的类型</typeparam>/// <param name="t">序列化的对象实例</param>/// <returns>序列化对象的字符串</returns>internal static string Object2XmlStringSerializer<T>(T t) where T : new(){try{// 创建包装类的实例并添加对象T wrapper = t;// 创建XmlSerializer的实例,指定根元素名称XmlSerializer serializer = new(typeof(T));// 使用StringWriter来捕获序列化后的XML内容using (StringWriter stringWriter = new()){serializer.Serialize(stringWriter, wrapper);string xmlContent = stringWriter.ToString();Debug.WriteLine(xmlContent);return xmlContent;}}catch (Exception ex){Debug.WriteLine("序列化失败");Exception exception = new("序列化失败");throw exception;}}
若需要指定编码方式,那么就需要转化为数据流,然后指定相应的编码方式后再读取,如下:
using (MemoryStream memoryStream = new ()){using (StreamWriter streamWriter = new (memoryStream, Encoding.UTF8)){serializer.Serialize(streamWriter, t);streamWriter.Flush();memoryStream.Position = 0;using (StreamReader streamReader = new (memoryStream, Encoding.UTF8)){string xmlContent = streamReader.ReadToEnd();Debug.WriteLine(xmlContent);return xmlContent;}}}
其实没有必要指定编码方式,从测试结果来看,反序列化时并不需要指定相应的编码方式,也可以正确进行解码。
xml字符串反序化为对象
/// <summary>
/// 将XML字符串反序列化为对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="xmlString"></param>
/// <returns></returns>
internal static T XmlStringDeserializer<T>(string xmlString) where T : new()
{// 使用StringReader包装XML字符串using StringReader stringReader = new(xmlString);try{XmlSerializer serializer = new(typeof(T));// 反序列化XML字符串T root = (T)serializer.Deserialize(stringReader);return root;}catch (Exception ex){throw ex;}
}
参考资料
《C# 核心技术指南(原书第7版)》
XML 序列化详细信息 - .NET | Microsoft Learn
这篇关于C#——XML序列化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!