如何忽略xmlns或兼容多命名空间,将XML反序列化为C#对象?
问题描述
有两个向后兼容的XML版本,仅根元素的xmlns属性值不同,需要将它们反序列化为同一个C#对象,但XmlSerializer对命名空间校验严格,导致反序列化失败。
示例XML
1.0版本
<?xml version="1.0"?> <HelloWorld xmlns="https://www.hello.ca/world/1.0"> </HelloWorld>
1.1版本
<?xml version="1.0"?> <HelloWorld xmlns="https://www.hello.ca/world/1.1"> </HelloWorld>
当前C#代码
实体类
[Serializable()] [System.ComponentModel.DesignerCategory("code")] [XmlType("HelloWorld")] public class HelloWorld { // 类成员定义... }
反序列化代码
XmlDocument xmlDoc = new XmlDocument(); xmlDoc.PreserveWhitespace = true; xmlDoc.Load("somewhere.xml"); using (XmlReader xr = new XmlNodeReader(xmlDoc)) { XmlSerializer xs = new XmlSerializer(typeof(HelloWorld)); var blah = xs.Deserialize(xr) as HelloWorld; return blah != null; }
报错信息
InvalidOperationException > There is an error in the XML document. > <HelloWorld xmlns='https://www.hello.ca/world/1.0'> was not expected.
解决方法
以下三种方案均可实现忽略命名空间或兼容多命名空间的需求:
方案一:自定义XmlReader忽略命名空间
创建继承自XmlTextReader的自定义阅读器,读取元素时清空命名空间,让XmlSerializer仅匹配元素名称。
public class NamespaceIgnoringXmlReader : XmlTextReader { public NamespaceIgnoringXmlReader(TextReader reader) : base(reader) { } public override string NamespaceURI => string.Empty; }
修改反序列化代码:
using (var reader = new StreamReader("somewhere.xml")) using (var xmlReader = new NamespaceIgnoringXmlReader(reader)) { XmlSerializer xs = new XmlSerializer(typeof(HelloWorld)); var blah = xs.Deserialize(xmlReader) as HelloWorld; return blah != null; }
方案二:提前修改XML的命名空间为统一值
加载XML后,将根元素的命名空间替换为实体类对应的固定值(或空值),再执行反序列化。
XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load("somewhere.xml"); // 将根元素命名空间设为空,或你指定的统一命名空间 xmlDoc.DocumentElement.SetAttribute("xmlns", string.Empty); using (XmlReader xr = new XmlNodeReader(xmlDoc)) { XmlSerializer xs = new XmlSerializer(typeof(HelloWorld)); var blah = xs.Deserialize(xr) as HelloWorld; return blah != null; }
如果实体类指定了固定命名空间(比如[XmlRoot(Namespace = "https://www.hello.ca/world/1.1")]),则将XML的命名空间替换为该值即可。
方案三:使用XmlSerializer重载构造函数指定多命名空间
通过XmlSerializer(Type, XmlRootAttribute[])构造函数,传入所有可能的根元素命名空间配置,让序列化器兼容多个命名空间。
首先在实体类上指定默认根节点配置(可选):
[Serializable()] [System.ComponentModel.DesignerCategory("code")] [XmlRoot("HelloWorld", Namespace = "https://www.hello.ca/world/1.1")] public class HelloWorld { // 类成员定义... }
修改反序列化代码:
// 定义所有可能的根元素命名空间 var possibleRoots = new[] { new XmlRootAttribute("HelloWorld") { Namespace = "https://www.hello.ca/world/1.0" }, new XmlRootAttribute("HelloWorld") { Namespace = "https://www.hello.ca/world/1.1" } }; // 创建支持多命名空间的序列化器 XmlSerializer xs = new XmlSerializer(typeof(HelloWorld), possibleRoots); using (var reader = new StreamReader("somewhere.xml")) { var blah = xs.Deserialize(reader) as HelloWorld; return blah != null; }
注意:该方式会生成多个临时序列化程序集,若对性能敏感,建议结合XmlSerializerCache或提前预生成序列化程序集。
内容的提问来源于stack exchange,提问作者codenamezero
相关产品推荐
相关产品推荐

