惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Project Zero
Project Zero
Security Archives - TechRepublic
Security Archives - TechRepublic
C
Cyber Attacks, Cyber Crime and Cyber Security
Security Latest
Security Latest
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
Vulnerabilities – Threatpost
C
CERT Recently Published Vulnerability Notes
S
Schneier on Security
G
GRAHAM CLULEY
L
Lohrmann on Cybersecurity
D
Darknet – Hacking Tools, Hacker News & Cyber Security
I
Intezer
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
F
Full Disclosure
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
WordPress大学
WordPress大学
Microsoft Azure Blog
Microsoft Azure Blog
H
Help Net Security
大猫的无限游戏
大猫的无限游戏
MyScale Blog
MyScale Blog
Hacker News: Ask HN
Hacker News: Ask HN
G
Google Developers Blog
H
Heimdal Security Blog
O
OpenAI News
Hugging Face - Blog
Hugging Face - Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
L
LangChain Blog
C
Cisco Blogs
云风的 BLOG
云风的 BLOG
IT之家
IT之家
Cyberwarzone
Cyberwarzone
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Know Your Adversary
Know Your Adversary
博客园 - 聂微东
The Cloudflare Blog
C
Check Point Blog
K
Kaspersky official blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
月光博客
月光博客
T
Tor Project blog
T
Threat Research - Cisco Blogs
T
Tailwind CSS Blog
P
Proofpoint News Feed
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
A
About on SuperTechFans
小众软件
小众软件
Cloudbric
Cloudbric
A
Arctic Wolf

博客园 - harrychinese

机器学习实战示例和基础知识查漏补缺 jupyter notebook上的java kernel 数据分析中几个常用的java类 我认为 C# 几个重要的设计缺陷 Jupyter notebook 最简安装方法 使用Tribuo分析波士顿房产数据 Java 语言常用的技巧 Java AI agent框架 java jbang 工具让java作为脚本语言 VS code + Jypyter 插件构建 Java notebook 环境 使用 VS code + Oracle java 插件搭建java语言原生的notebook环境 【计算机科学速成课】[40集全/精校] - Crash Course Computer Science Handy toolbox for IT developers 中学学校教材下载 深度学习模型训练5个关键步骤 使用SpringBoot实现一个执行通用SQL的Rest接口 学益升X1和国悦K3阅读器 状态机 使用场景规则匹配模式代替复杂的if else条件判断 vscode 数据处理的插件 iPhone XR 升级非最新版IOS duckdb nuget打包部署完整步骤 ML.Net机器学习基本知识 机器学习资料汇总 优化算法知识和类库 画草图和示意图的好工具 .net debug tools 运筹学/最优化讲义.pdf Lazarus
C# XML 序列化器
harrychinese · 2025-03-18 · via 博客园 - harrychinese

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 提供了两种输出控制模式:

  1. ClassicMode: Classic模式下List或Dictionary属性不输出容器的 Capacity 等属性, 但同时也会干扰 null 属性的强制输出
  2. 非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);
    }
}