C# XML 序列化器
Json序列化工具可选择的很多, 但XML序列化器可选择的不多, 总体推荐 ExtendedXmlSerializer. 微软官方的 DataContractSerializer 尤其不太好用, 时不时在 xml element 上莫名其妙增加一些namespace信息.
ExtendedXmlSerializer
支持.Net Framework和 core, 同时部分兼容 XmlSerializer XML Tag Attribute, 支持 List/Dictionary 集合属性的序列化。
https://extendedxmlserializer.github.io/
https://github.com/ExtendedXmlSerializer/home/wiki/The-Basics
DataContractSerializer
微软官方
支持 私有字段 的序列化。
支持 List/Dictionary 属性序列化
老的 WCF 官方使用的序列化器
https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-runtime-serialization-datacontractserializer
https://www.albahari.com/nutshell/DataContractSerializer.pdf
XmlSerializer
微软官方
不支持 List/Dictionary 属性序列化,
不支持 私有字段 的序列化。
https://peterdaugaardrasmussen.com/2017/09/20/differences-between-datacontractserializer-and-xmlserializer/
SharpSerializer
需要一个比 DataContractSerializer 更简单、更快速的 XML 序列化库。
支持 私有字段 的序列化。
https://github.com/polenter/SharpSerializer
https://www.sharpserializer.net/en/tutorial/index.html
XSerializer
需要支持 加密、压缩、多态、接口支持。
需要比 DataContractSerializer 更强的序列化能力。
https://github.com/bfriesen/XSerializer
ExtendedXmlSerializer 使用代码示例
ExtendedXmlSerializer 默认会将类中所有public的属性和字段做序列化, 除非为public属性打上 [XmlIgnore] 标签.
IExtendedXmlSerializer 提供了两种输出控制模式:
- ClassicMode: Classic模式下List或Dictionary属性不输出容器的 Capacity 等属性, 但同时也会干扰 null 属性的强制输出
- 非ClassicMode: null属性的强制输出可以很完美, 但会自动输出List或Dictionary容器的 Capacity 等属性
推荐使用非ClassicMode, 因为在XML去除 Capacity tag 非常简单. 下面是一个代码片段
string xml22 = @"<Root><Capacity>100</Capacity><a>sssssss<Capacity/></a></Root>";
var xDoc= XDocument.Parse(xml22);
xDoc.Descendants("Capacity").Remove();
Console.WriteLine(xDoc.ToString());
ExtendedXmlSerializer示例代码:
using ExtendedXmlSerializer;
using ExtendedXmlSerializer.Configuration;
using ExtendedXmlSerializer.ContentModel.Content;
using ExtendedXmlSerializer.ExtensionModel.Xml;
using ExtendedXmlSerializer.ReflectionModel;
using System.Linq.Expressions;
using System.Xml;
using System.Xml.Serialization;
using ExtendedXmlSerializer;
using ExtendedXmlSerializer.Configuration;
using ExtendedXmlSerializer.ContentModel.Content;
using ExtendedXmlSerializer.ExtensionModel.Xml;
using ExtendedXmlSerializer.ReflectionModel;
using System.Linq.Expressions;
using System.Xml;
using System.Xml.Serialization;
[XmlRoot("DemoRoot")] // 根节点重命名
class Demo
{
[XmlIgnore] //序列化时忽略指定的属性
public String? ignoredField { get; set; }
[Verbatim] //输出的文字嵌入到 CDATA 中
public string? cdataField { get; set; }
[XmlElement("renamedField")] //重命名 xml tag
public string? renamedField { get; set; }
public string? nullField { get; set; }
public int intField { get; set; }
public Int32 intField2 { get; set; }
public List<String> strList { get; set; } = new List<string>();
public string getMessage()
{
return "Hello";
}
/// <summary>
/// 在这里设置强制需要输出XML的字段, 以便将这些信息在初始化IExtendedXmlSerializer时告知 Serializer
/// </summary>
/// <returns></returns>
public static List<Expression<Func<Demo, string>>> GetForceOutputMembers()
{
var memberExpressions = new List<Expression<Func<Demo, string>>>();
memberExpressions.Add(x => x.nullField);
memberExpressions.Add(x => x.renamedField);
return memberExpressions;
}
}
[XmlRoot("DemoRoot")] // 根节点重命名
class RestoredDemo
{
public int intField { get; set; }
public Int32 intField233 { get; set; }
public List<String> strList { get; set; } = new List<string>();
}
public class Address
{
public string location { get; set; }
}
public class Person
{
public List<string> Hobbies { get; set; } // List<string> 属性
public List<Address> addresses { get; set; }
}
public class MyClass
{
public string Name { get; set; }
public int? Age { get; set; }
}
class Test
{
public static void Main()
{
BasicTest();
//ListElementTest();
}
public static void BasicTest()
{
Expression<Func<Demo, string>> forceOutputMember = x => x.nullField;
//Expression<Func<Demo, string>> forceOutputMember= Demo.GetForceOutputMembers()[0];
//创建 IExtendedXmlSerializer 的配置类
var config = new ConfigurationContainer()
.EnableImplicitTypingByInspecting<Demo>() //在反序列化时一定要调用这个方法, 否则大概率会因创建的对象和指定的类不一致而抛异常
.UseOptimizedNamespaces() //输出更简洁的XML namespace写法
.EnableClassicMode() //Classic模式下List或Dictionary属性不输出容器的 Capacity 等属性, 但同时也会干扰 null 属性的强制输出
//.Emit(EmitBehaviors.Always) //强制所有属性输出至XML, 即使它们的取值为null, 注意: Classic模式下不生效
.WithUnknownContent().Continue() //增加XML反序列的容错性
.EnableImplicitTyping(typeof(Demo), typeof(List<>)) //序列化到XML时不输出指定C#对象命名空间
.Type<Demo>() //返回目标类的 ITypeConfiguration, 信息, 以便完成根节点/子节点重命名等后序链式调用
.Name("DemoRoot") //根节点重命名, 推荐使用 XmlRoot Attribute
//.Member(x => x.renamedField).Name("renamedField") //内部节点重命名, 推荐使用 XmlElement Attribute
//.Member(x => x.nullField).EmitWhen(x => true) // 如果某个字段值为null, 可以强制序列化到XML, 注意: Classic模式下仍能生效
.Member(forceOutputMember).EmitWhen(x => true) // 如果某个字段值为null, 可以强制序列化到XML, 注意: Classic模式下仍能生效
;
// 使用遍历的方式无法强制将null属性输出到XML, 不管是否在Classic模式下
foreach (var property in typeof(MyClass).GetProperties())
{
//config.Member(x => x.nullField).EmitWhen(x => true); //Member()如使用Lambda 参数, 可以强制将null属性输出到XML
config.Member(property).EmitWhen(x => true); //Member()如使用 property 参数, 无法强制将null属性输出到XML, 不管是否在Classic模式下, 看起来是个bug
}
IExtendedXmlSerializer extendedXmlSerializer = config.Create();
Demo demo = new Demo();
demo.intField = 100;
demo.intField2 = 200;
demo.ignoredField = "ignoredField";
demo.cdataField = "cdataField";
demo.renamedField = null;//"renamedField";
demo.nullField = null; //当取值为 null 时候将不会输出到 xml中, 所以需要初始化成空字符串
demo.strList.Add("item1");
demo.strList.Add("item2");
//init XmlWriterSettings
var settings = new System.Xml.XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true; //省略 xml declare 头
//序列化
string xml = extendedXmlSerializer.Serialize(settings, demo);
xml = xml.Replace("xsi:nil=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"", "");
xml = xml.Replace(" />", "/>");
Console.WriteLine(xml);
IExtendedXmlSerializer extendedXmlSerializer2 = new ConfigurationContainer()
.EnableImplicitTypingByInspecting<Demo>() //在反序列化时一定要调用这个方法, 否则大概率会因创建的对象和指定的类不一致而抛异常
.UseOptimizedNamespaces()
.WithUnknownContent().Continue() //增加XML反序列的容错性
.EnableImplicitTyping(typeof(Demo)) //序列化到XML时不输出指定C#对象命名空间
.Create();
var demo2 = extendedXmlSerializer2.Deserialize<Demo>(xml);
Console.WriteLine(demo2.intField);
Console.WriteLine(demo2.strList[0]);
if (demo2.nullField == null)
{
Console.WriteLine("nullField is null");
}
else
{
Console.WriteLine($"nullField:{demo2.nullField}.");
}
}
public static void ListElementTest()
{
//测试List成员序列化
var person = new Person
{
Hobbies = new List<string> { "Reading", "Gaming", "Coding" },
addresses = new List<Address> { new Address() { location = "SH" }, new Address() { location = "BJ" } }
};
// 创建序列化器
var serializer = new ConfigurationContainer()
.UseOptimizedNamespaces()
.WithUnknownContent().Continue() //增加XML反序列的容错性
.EnableImplicitTyping(typeof(Person), typeof(Address)) // 注册类型
.EnableClassicMode() //Classic模式下List或Dictionary属性不输出容器的 Capacity 等属性, 但同时也会干扰 null 属性的强制输出
.Create();
//init XmlWriterSettings
var settings = new System.Xml.XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true; //省略 xml declare 头
// 序列化为 XML
string xml3 = serializer.Serialize(settings, person);
Console.WriteLine(xml3);
var person2 = serializer.Deserialize<Person>(xml3);
Console.WriteLine(person2.Hobbies[0]);
Console.WriteLine(person2.addresses[1].location);
}
}

浙公网安备 33010602011771号