Dotnet使用System.Xml.Serialization处理Xml序列化

命名空间

作为一种归纳方式,便于后续命名空间的快速引入。

cs 复制代码
 /// <summary>
 /// 定义 命名空间常量
 /// </summary>
 public static class WpmlNamespace
 {
     public const string Wpml = "http://www.dji.com/wpmz/1.0.2";
     public const string Kml = "http://www.opengis.net/kml/2.2";
 }

根节点

使用[XmlRoot] 、自定义根节点属性[XmlAttribute]以及配置子节点[XmlElement]

cs 复制代码
    [XmlRoot("kml", Namespace = WpmlNamespace.Kml)]
    public class KmlRoot
    {
        [XmlElement("Document", Namespace = WpmlNamespace.Kml)]
        public Document Document { get; set; }

        [XmlAttribute("wpml", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string WpmlNamespaceAttribute { get; set; } = WpmlNamespace.Wpml;
    }

子节点类型

使用[XmlElement] 特性,进行子节点序列名称以及对应的命名空间。

cs 复制代码
//[XmlType("Document", Namespace = WpmlNamespace.Kml)]
public class Document
{
    // 创建信息 (Document的子元素)
    [XmlElement("author", Namespace = WpmlNamespace.Wpml)]
    public string Author { get; set; } // 非必需

    [XmlElement("createTime", Namespace = WpmlNamespace.Wpml)]
    public long? CreateTime { get; set; } // 非必需, ms 时间戳

    [XmlElement("updateTime", Namespace = WpmlNamespace.Wpml)]
    public long? UpdateTime { get; set; } // 非必需, ms 时间戳
}

填充数据

逐步构建实例数据,用于后续查看序列化效果。

cs 复制代码
// 创建KML内容的逻辑
KmlRoot kmlRoot = new KmlRoot();
// Document 节点
Document document = new Document();
kmlRoot.Document = document;
document.CreateTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
document.UpdateTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();

序列化输出

使用XmlSerializerXmlWriterSettingsStringWriter以及XmlWriter生成序列数据。

cs 复制代码
// 构建序列化实例
var serializer = new XmlSerializer(typeof(KmlRoot), null,null, null,null);
var settings = new XmlWriterSettings // 序列化配置
{
    Indent = true, // 启用自定义间隔
    IndentChars = "  ",// 属性间隔
    Encoding = Encoding.UTF8,// 设置编码格式
    OmitXmlDeclaration = false, // 保持 XML 声明
};
using (var stringWriter = new System.IO.StringWriter())
using (var xmlWriter = XmlWriter.Create(stringWriter, settings))
{
    // 序列化对象
    serializer.Serialize(xmlWriter, kmlRoot);
    // 获取序列化后的字符串
    var xmlString = stringWriter.ToString();
    return xmlString;
}

序列化结果如下:

xml 复制代码
<?xml version="1.0" encoding="utf-16"?>
<kml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wpml="http://www.dji.com/wpmz/1.0.2" xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <wpml:createTime>1761753196268</wpml:createTime>
    <wpml:updateTime>1761753196269</wpml:updateTime>
  </Document>
</kml>

修改xml声明

可以看到序列化输出结果中,当前文本的文本编码说明为UTF-16,实际需要的是UTF-8,原始文本编码和目标编码如下。

原始文本编码。

xml 复制代码
<?xml version="1.0" encoding="utf-16"?>

目标文本编码。

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>

解决办法是继承StringWriter 类,重写编码属性。

cs 复制代码
// 设置文本编码为utf-8
public class Utf8StringWriter: System.IO.StringWriter
{
    public override Encoding Encoding => Encoding.UTF8;
}

替换序列化代码中的StringWriter 实例构造。

cs 复制代码
//using (var stringWriter = new StringWriter())
using (var stringWriter = new Utf8StringWriter())
using (var xmlWriter = XmlWriter.Create(stringWriter, settings))
{
    // 序列化对象
    serializer.Serialize(xmlWriter, kmlRoot);
    // 获取序列化后的字符串
    var xmlString = stringWriter.ToString();
    return xmlString;
}

输出结果如下:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<kml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wpml="http://www.dji.com/wpmz/1.0.2" xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <wpml:createTime>1761753196268</wpml:createTime>
    <wpml:updateTime>1761753196269</wpml:updateTime>
  </Document>
</kml>

xml大写声明编码值

当前小写编码值:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>

期望大写编码值:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>

自定义UTF8Encoding编码类。

cs 复制代码
/// <summary>
/// 自定义大写的 UTF8Encoding
/// </summary>
public class CustomUTF8Encoding : UTF8Encoding
{
    public override string HeaderName => base.HeaderName.ToUpper();
    public override string WebName => base.WebName.ToUpper();
    public override string BodyName => base.BodyName.ToUpper();
}

修改Utf8StringWriter类代码。

cs 复制代码
public class Utf8StringWriter: System.IO.StringWriter
{
    public Utf8StringWriter()
    {
        
    }
    public override Encoding Encoding => new CustomUTF8Encoding();
}

不显示默认值属性

对于未赋值字段,默认会显示并标记为xsi:nil="true",这在某些场景并不合适,需要进行去除。

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<kml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wpml="http://www.dji.com/wpmz/1.0.2" xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <wpml:createTime>1761899701431</wpml:createTime>
    <wpml:updateTime>1761899701431</wpml:updateTime>
    <wpml:missionConfig>
      <wpml:executeRCLostAction xsi:nil="true" />
      <wpml:takeOffSecurityHeight>0</wpml:takeOffSecurityHeight>
      <wpml:takeOffRefPointAGLHeight xsi:nil="true" />
      <wpml:globalTransitionalSpeed>0</wpml:globalTransitionalSpeed>
      <wpml:globalRTHHeight xsi:nil="true" />
    </wpml:missionConfig>
  </Document>
</kml>

对应属性修改为完整属性。

cs 复制代码
        private double? takeOffRefPointAGLHeight;
        
        [XmlElement("takeOffRefPointAGLHeight", Namespace = WpmlNamespace.Wpml)]
        public double? TakeOffRefPointAGLHeight { get => takeOffRefPointAGLHeight; set => takeOffRefPointAGLHeight = value; } // 非必需, m

添加函数public bool ShouldSerialize[对应的属性名称](),此处属性为TakeOffRefPointAGLHeight对应函数如下。函数内部实际按照需求进行判定,返回false则表示序列化中不显示,反之亦然。

cs 复制代码
        public bool ShouldSerializeTakeOffRefPointAGLHeight()  => takeOffRefPointAGLHeight.HasValue;

去除自带命名空间

去除默认xml 中命名空间,如:xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"以及 xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xml 复制代码
<kml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wpml="http://www.dji.com/wpmz/1.0.2" xmlns="http://www.opengis.net/kml/2.2">
</kml>

期望输出:

xml 复制代码
<kml xmlns:wpml="http://www.dji.com/wpmz/1.0.2" xmlns="http://www.opengis.net/kml/2.2">
</kml>

可以尝试使用XmlSerializerNamespaces 序列化时,进行命名空间置空。

cs 复制代码
//using (var stringWriter = new StringWriter())
using (var stringWriter = new Utf8StringWriter())
using (var xmlWriter = XmlWriter.Create(stringWriter, settings))
{
    var namespaces = new XmlSerializerNamespaces();
    namespaces.Add("", ""); // 添加空命名空间
    // 序列化对象
    serializer.Serialize(xmlWriter, kmlRoot,namespaces);
    // 获取序列化后的字符串
    var xmlString = stringWriter.ToString();
    return xmlString;
}

序列化如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns:wpml="http://www.dji.com/wpmz/1.0.2" xmlns="http://www.opengis.net/kml/2.2">
  <Document>
  </Document>
</kml>

节点子类替换父类

使用过程中,多类型嵌套时,部分节点存在之类,需要在序列化时进行替换,此时需要引入XmlAttributeOverrides,在构造序列化实例时进行类型重写。

父类类型。

cs 复制代码
public class ActionActuatorFuncParam
{
	
}

子类类型。

cs 复制代码
public class OrientedShoot : ActionActuatorFuncParam
{
	[XmlElement("focusX", Namespace = WpmlNamespace.Wpml)]
    public int FocusX { get; set; } // 必需
}

public class GimbalRotate : ActionActuatorFuncParam
{
	
}

实际使用类。

cs 复制代码
// KML 根元素
[XmlRoot("kml", Namespace = WpmlNamespace.Kml)]
public class KmlRoot
{
    [XmlElement("Document", Namespace = WpmlNamespace.Kml)]
    public Document Document { get; set; }
}

// 省略n级别嵌套类

[XmlType("action", Namespace = WpmlNamespace.Wpml)]
public class WpmlAction
{
    [XmlElement("actionActuatorFuncParam", Namespace = WpmlNamespace.Wpml)]
    public WpmlActionActuatorFuncParam ActionActuatorFuncParam { get; set; }
}

数据赋值。

cs 复制代码
KmlRoot kmlRoot = new KmlRoot();
// 省略中间赋值
OrientedShoot actionActuatorFuncParam = new OrientedShoot();
action.ActionActuatorFuncParam = actionActuatorFuncParam;
actionActuatorFuncParam.FocusX = 12;// 进行属性赋值

进行重写设置。

cs 复制代码
var attrs = new XmlAttributes();
var attr = new XmlElementAttribute("actionActuatorFuncParam", typeof(OrientedShoot)) { Namespace = WpmlNamespace.Wpml };
attrs.XmlElements.Add(attr);
// Adds the element to the collection of elements.  
// Creates the XmlAttributeOverrides instance.  
XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();
// 指定需要替换类型的实际属性名称,不建议手动字符串拼写
attrOverrides.Add(typeof(WpmlAction), nameof(WpmlAction.ActionActuatorFuncParam), attrs);
//构建序列化实例(传入序列化化实例类型)
var serializer = new XmlSerializer(typeof(KmlRoot), overrides,null, new XmlRootAttribute("kml") { Namespace = WpmlNamespace.Kml },null);
var settings = new XmlWriterSettings
{
    Indent = true,
    IndentChars = "  ",
    Encoding = Encoding.UTF8,
    OmitXmlDeclaration = false, // 保持 XML 声明
};

using (var stringWriter = new StringWriter())
using (var xmlWriter = XmlWriter.Create(stringWriter, settings))
{
    // 序列化对象
    serializer.Serialize(xmlWriter, kmlRoot);
    // 获取序列化后的字符串
    var xmlString = stringWriter.ToString();
    return xmlString;
}

序列化结果如下:

xml 复制代码
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.6">
  <Document>
    <Folder>
      <Placemark>
        <wpml:actionGroup>
          <wpml:action>
            <!--子类型序列化重写-->
            <wpml:actionActuatorFuncParam>
              <wpml:focusX>0</wpml:focusX>
            </wpml:actionActuatorFuncParam>
            <!--子类型序列化重写-->
          </wpml:action>
        </wpml:actionGroup>
      </Placemark>
    </Document>
 </kml>
相关推荐
d111111111d33 分钟前
在STM32中,中断服务函数的命名有什么要求?
笔记·stm32·单片机·嵌入式硬件·学习·c#
墨痕诉清风2 小时前
java漏洞集合工具(Struts2、Fastjson、Weblogic(xml)、Shiro、Log4j、Jboss、SpringCloud)
xml·java·struts·安全·web安全·spring cloud·log4j
极客智造2 小时前
深入解析 C# Type 类:解锁反射与动态编程的核心
c#·反射
SmoothSailingT2 小时前
C#——textBox控件(1)
开发语言·c#
superman超哥3 小时前
仓颉语言中并发集合的实现深度剖析与高性能实践
开发语言·后端·python·c#·仓颉
工程师0073 小时前
C#中的服务注册剖析
c#·服务注册
张人玉3 小时前
c#DataTable类
数据库·c#
缺点内向3 小时前
如何在 C# .NET 中将 Markdown 转换为 PDF 和 Excel:完整指南
pdf·c#·.net·excel
CodeCraft Studio3 小时前
Excel处理控件Aspose.Cells教程:使用C#在Excel中创建旭日图
c#·excel·aspose·excel旭日图·excel库·excel开发控件·excel api库