C# XmlSerializer反序列化无默认命名空间XML无法正确填充类解决方案咨询
解决方案
方案1:修改实体类特性适配多命名空间(改动最小,无需调整反序列化逻辑)
你可以为需要兼容空命名空间和my_test_ns1命名空间的属性,添加多个[XmlElement]特性,同时匹配两种命名空间的节点:
[XmlRoot(ElementName = "root", Namespace = "my_test_ns1")] public class Test { // 同时匹配带d:前缀(my_test_ns1)和无前缀(空命名空间)的name节点 [XmlElement(Namespace = "")] [XmlElement(Namespace = "my_test_ns1")] public string name { get; set; } [XmlElement(Namespace = "")] [XmlElement(Namespace = "my_test_ns1")] public int age { get; set; } [XmlElement(Namespace = "my_test_ns2")] public int ageInMonths { get; set; } // 其余原有代码保持不变 }
修改后运行代码,xml1和xml2都可以正常反序列化。
方案2:自定义XmlReader做命名空间重定向(无需修改实体类)
如果不希望改动实体类定义,可以在反序列化前用自定义的XmlReader,将根节点下的空命名空间节点自动映射为my_test_ns1命名空间,适配第三方接口的返回格式:
// 基础XmlReader包装类,将所有逻辑代理给内部Reader public abstract class XmlReaderWrapper : XmlReader { protected readonly XmlReader _inner; protected XmlReaderWrapper(XmlReader inner) => _inner = inner; public override int AttributeCount => _inner.AttributeCount; public override string BaseURI => _inner.BaseURI; public override int Depth => _inner.Depth; public override bool EOF => _inner.EOF; public override bool IsEmptyElement => _inner.IsEmptyElement; public override string LocalName => _inner.LocalName; public override string NamespaceURI => _inner.NamespaceURI; public override XmlNameTable NameTable => _inner.NameTable; public override XmlNodeType NodeType => _inner.NodeType; public override string Prefix => _inner.Prefix; public override ReadState ReadState => _inner.ReadState; public override string Value => _inner.Value; public override string GetAttribute(string name) => _inner.GetAttribute(name); public override string GetAttribute(string name, string namespaceURI) => _inner.GetAttribute(name, namespaceURI); public override string GetAttribute(int i) => _inner.GetAttribute(i); public override string LookupNamespace(string prefix) => _inner.LookupNamespace(prefix); public override bool MoveToAttribute(string name) => _inner.MoveToAttribute(name); public override bool MoveToAttribute(string name, string ns) => _inner.MoveToAttribute(name, ns); public override bool MoveToElement() => _inner.MoveToElement(); public override bool MoveToFirstAttribute() => _inner.MoveToFirstAttribute(); public override bool MoveToNextAttribute() => _inner.MoveToNextAttribute(); public override bool Read() => _inner.Read(); public override bool ReadAttributeValue() => _inner.ReadAttributeValue(); public override void ResolveEntity() => _inner.ResolveEntity(); } // 自定义命名空间映射Reader public class NamespaceMappingReader : XmlReaderWrapper { private readonly string _rootNs; private readonly string _targetDefaultNs; public NamespaceMappingReader(XmlReader inner, string rootNs, string targetDefaultNs) : base(inner) { _rootNs = rootNs; _targetDefaultNs = targetDefaultNs; } public override string NamespaceURI { get { // 根节点直接返回原有命名空间 if (Depth == 0) return base.NamespaceURI; // 根节点下的空命名空间节点,替换为目标默认命名空间 if (base.NamespaceURI == "" && base.LookupNamespace("d") == _rootNs) { return _targetDefaultNs; } return base.NamespaceURI; } } }
修改Run方法的反序列化逻辑即可:
public void Run(string str, string name) { XmlSerializer serializer = new XmlSerializer(typeof(Test)); using (StringReader rStream = new StringReader(str)) using (XmlReader innerReader = XmlReader.Create(rStream)) using (XmlReader reader = new NamespaceMappingReader(innerReader, "my_test_ns1", "my_test_ns1")) { Test test = serializer.Deserialize(reader) as Test; Console.Out.WriteLine(test.Equals(this) ? $"{name} equals expected" : $"{name} does not equal expected"); } }
该方案会在读取XML节点时自动修正命名空间,完全不需要调整实体类,也不会影响其他正常命名空间的节点匹配。
内容的提问来源于stack exchange,提问作者Chuck Larson
相关产品推荐
相关产品推荐

