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>
相关推荐
Dolphin_Home5 小时前
轻量实用的 XML 与 JSON / 对象互转工具类(Jackson 实现)
xml·java·json
聪明努力的积极向上6 小时前
【.net framework】WINDOWS服务和控制台程序简单介绍
windows·.net
歪歪1007 小时前
在C#中除了按属性排序,集合可视化器还有哪些辅助筛选的方法?
开发语言·前端·ide·c#·visual studio
weixin_307779137 小时前
C#程序实现将Teradata的存储过程转换为Snowflake的sql的存储过程
数据库·数据仓库·c#·云计算·迁移学习
李高钢7 小时前
c#获取当前程序所在目录避坑
开发语言·数据库·c#
Victory_20257 小时前
c# stateless介绍
c#
gc_22998 小时前
学习C#调用OpenXml操作word文档的基本用法(3:Style类分析-1)
c#·style·openxml
还是大剑师兰特9 小时前
C#面试题及详细答案120道(51-60)-- LINQ与Lambda
c#·大剑师
loong_XL11 小时前
AC自动机算法-字符串搜索算法:敏感词检测
开发语言·算法·c#