C#序列化对象到XML:保留Name元素XML文本不转义
解决方案:动态控制Xml序列化时的字符转义
这个问题其实是Xml序列化里常见的「选择性不转义特殊字符」需求,默认的XmlSerializer会自动转义所有XML特殊字符(比如<、>、&),但我们可以通过两种方式实现你要的动态处理效果——既支持直接输出XML标签内容,也兼容普通文本的正常序列化。
方法1:实现IXmlSerializable接口自定义序列化逻辑
通过实现IXmlSerializable,我们可以完全掌控序列化/反序列化的过程,根据Name属性的内容动态决定是输出原始XML片段还是转义后的文本。
代码示例
using System.IO; using System.Xml; using System.Xml.Serialization; public class ObjSer : IXmlSerializable { public string Name { get; set; } // 实现IXmlSerializable的接口方法,返回null即可 public XmlSchema GetSchema() => null; // 反序列化逻辑(如果需要支持从XML还原对象,可按需求实现) public void ReadXml(XmlReader reader) { reader.ReadStartElement(); if (!reader.IsEmptyElement) { Name = reader.NodeType == XmlNodeType.Element ? reader.ReadInnerXml() : reader.ReadContentAsString(); } reader.ReadEndElement(); } // 核心:自定义序列化逻辑 public void WriteXml(XmlWriter writer) { writer.WriteStartElement("Name"); try { // 尝试把Name内容当作XML片段写入 using var stringReader = new StringReader(Name); using var xmlReader = XmlReader.Create(stringReader, new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment }); writer.WriteNode(xmlReader, true); } catch (XmlException) { // 解析失败(说明是普通文本),则写入转义后的字符串 writer.WriteString(Name); } writer.WriteEndElement(); } }
测试验证
// 场景1:带XML标签的内容 var objWithTags = new ObjSer { Name = "<tag1>Value</tag1>" }; using var sw1 = new StringWriter(); new XmlSerializer(typeof(ObjSer)).Serialize(sw1, objWithTags); Console.WriteLine(sw1.ToString()); // 输出:<ObjSer><Name><tag1>Value</tag1></Name></ObjSer> // 场景2:普通文本内容 var objPlainText = new ObjSer { Name = "Value" }; using var sw2 = new StringWriter(); new XmlSerializer(typeof(ObjSer)).Serialize(sw2, objPlainText); Console.WriteLine(sw2.ToString()); // 输出:<ObjSer><Name>Value</Name></ObjSer>
方法2:使用辅助属性配合XmlAnyElement
如果不想修改整个类的序列化逻辑,可以用「忽略原属性+辅助XmlNode属性」的方式,只针对Name字段做特殊处理,代码更简洁。
代码示例
using System.Xml; using System.Xml.Serialization; public class ObjSer { // 用XmlIgnore忽略原属性,不参与序列化 [XmlIgnore] public string Name { get; set; } // 辅助属性,负责实际的序列化/反序列化 [XmlElement("Name")] public XmlNode NameAsXml { get { if (string.IsNullOrEmpty(Name)) return null; try { // 尝试将Name解析为XML片段,返回对应的元素节点 var doc = new XmlDocument(); doc.LoadXml($"<temp>{Name}</temp>"); return doc.DocumentElement.FirstChild; } catch (XmlException) { // 解析失败,返回文本节点(自动处理转义) var doc = new XmlDocument(); return doc.CreateTextNode(Name); } } set { // 反序列化时还原Name属性 if (value == null) { Name = null; } else if (value is XmlText textNode) { Name = textNode.Value; } else { Name = value.OuterXml; } } } }
效果说明
这个方法的核心是利用XmlNode类型的特性:如果是元素节点,XmlSerializer会直接输出节点的原始XML内容;如果是文本节点,则会自动处理特殊字符转义,完美适配你的两种场景。
两种方法对比
- 方法1:灵活性极高,适合需要对整个类的序列化做更多定制的场景,但需要自己实现完整的序列化/反序列化逻辑。
- 方法2:代码更简洁,仅针对单个属性处理,侵入性低,适合只需要定制个别字段的场景。
内容的提问来源于stack exchange,提问作者Vishnu
相关产品推荐
相关产品推荐

