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

命名空间
作为一种归纳方式,便于后续命名空间的快速引入。
/// <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]。
[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] 特性,进行子节点序列名称以及对应的命名空间。
//[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 时间戳
}
填充数据
逐步构建实例数据,用于后续查看序列化效果。
// 创建KML内容的逻辑
KmlRoot kmlRoot = new KmlRoot();
// Document 节点
Document document = new Document();
kmlRoot.Document = document;
document.CreateTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
document.UpdateTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
序列化输出
使用XmlSerializer,XmlWriterSettings,StringWriter以及XmlWriter生成序列数据。
// 构建序列化实例
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 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 version="1.0" encoding="utf-16"?>
目标文本编码。
<?xml version="1.0" encoding="utf-8"?>
解决办法是继承StringWriter 类,重写编码属性。
// 设置文本编码为utf-8
public class Utf8StringWriter: System.IO.StringWriter
{public override Encoding Encoding => Encoding.UTF8;
}
替换序列化代码中的StringWriter 实例构造。
//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 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 version="1.0" encoding="utf-8"?>
期望大写编码值:
<?xml version="1.0" encoding="UTF-8"?>
自定义UTF8Encoding编码类。
/// <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类代码。
public class Utf8StringWriter: System.IO.StringWriter
{public Utf8StringWriter(){}public override Encoding Encoding => new CustomUTF8Encoding();
}
不显示默认值属性
对于未赋值字段,默认会显示并标记为xsi:nil="true",这在某些场景并不合适,需要进行去除。
<?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>
对应属性修改为完整属性。
private double? takeOffRefPointAGLHeight;[XmlElement("takeOffRefPointAGLHeight", Namespace = WpmlNamespace.Wpml)]public double? TakeOffRefPointAGLHeight { get => takeOffRefPointAGLHeight; set => takeOffRefPointAGLHeight = value; } // 非必需, m
添加函数public bool ShouldSerialize[对应的属性名称](),此处属性为TakeOffRefPointAGLHeight对应函数如下。函数内部实际按照需求进行判定,返回false则表示序列化中不显示,反之亦然。
public bool ShouldSerializeTakeOffRefPointAGLHeight() => takeOffRefPointAGLHeight.HasValue;
去除自带命名空间
去除默认xml 中命名空间,如:xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"以及 xmlns:xsd="http://www.w3.org/2001/XMLSchema"。
<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>
期望输出:
<kml xmlns:wpml="http://www.dji.com/wpmz/1.0.2" xmlns="http://www.opengis.net/kml/2.2">
</kml>
可以尝试使用XmlSerializerNamespaces 序列化时,进行命名空间置空。
//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 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,在构造序列化实例时进行类型重写。
父类类型。
public class ActionActuatorFuncParam
{}
子类类型。
public class OrientedShoot : ActionActuatorFuncParam
{[XmlElement("focusX", Namespace = WpmlNamespace.Wpml)]public int FocusX { get; set; } // 必需
}public class GimbalRotate : ActionActuatorFuncParam
{}
实际使用类。
// 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; }
}
数据赋值。
KmlRoot kmlRoot = new KmlRoot();
// 省略中间赋值
OrientedShoot actionActuatorFuncParam = new OrientedShoot();
action.ActionActuatorFuncParam = actionActuatorFuncParam;
actionActuatorFuncParam.FocusX = 12;// 进行属性赋值
进行重写设置。
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;
}
序列化结果如下:
<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>
