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
需要支持 加密、壓縮、多態(tài)、接口支持。
需要比 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")] // 根節(jié)點重命名
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")] // 根節(jié)點重命名
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];
//創(chuàng)建 IExtendedXmlSerializer 的配置類
var config = new ConfigurationContainer()
.EnableImplicitTypingByInspecting<Demo>() //在反序列化時一定要調用這個方法, 否則大概率會因創(chuàng)建的對象和指定的類不一致而拋異常
.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, 信息, 以便完成根節(jié)點/子節(jié)點重命名等后序鏈式調用
.Name("DemoRoot") //根節(jié)點重命名, 推薦使用 XmlRoot Attribute
//.Member(x => x.renamedField).Name("renamedField") //內部節(jié)點重命名, 推薦使用 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>() //在反序列化時一定要調用這個方法, 否則大概率會因創(chuàng)建的對象和指定的類不一致而拋異常
.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" } }
};
// 創(chuàng)建序列化器
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號