XML反序列化时设置忽略命名空间仍无法跳过根节点命名空间如何解决
报错原因
你设置reader.Namespaces = false后,读取器不会解析命名空间前缀,会直接把前缀和本地名拼接作为节点的完整名称,所以<tns:JPK>会被识别为节点名是tns:JPK,和你的Jpk类默认映射的JPK节点名不匹配,因此抛出未预期节点的异常。
解决方案
以下两种方案均可实现完全忽略命名空间、前缀的反序列化需求:
方案1:自定义XmlReader实现流式忽略命名空间(性能最优)
继承XmlTextReader重写核心属性,自动截断节点前缀、清空命名空间信息,不需要修改XML原文,内存占用低:
public class NamespaceIgnorantXmlReader : XmlTextReader { public NamespaceIgnorantXmlReader(TextReader reader) : base(reader) { } public override string LocalName { get { var rawName = base.Name; var colonIndex = rawName.IndexOf(':'); return colonIndex < 0 ? rawName : rawName.Substring(colonIndex + 1); } } public override string NamespaceURI => ""; public override string Prefix => ""; }
修改你的反序列化方法使用该自定义读取器即可:
private Jpk DeserializeInputFile(string path) { var fileContent = File.ReadAllText(path); using (var textReader = new StringReader(fileContent)) using (var reader = new NamespaceIgnorantXmlReader(textReader)) { reader.Namespaces = false; var serializer = new XmlSerializer(typeof(Jpk)); return (Jpk)serializer.Deserialize(reader); } }
方案2:预处理XML清除所有命名空间(易调试)
如果不想自定义读取器,可以先加载XML为XDocument,遍历清除所有命名空间、前缀后再反序列化:
private Jpk DeserializeInputFile(string path) { var doc = XDocument.Load(path); // 移除所有元素的命名空间和前缀 foreach (var element in doc.Descendants()) { element.Name = element.Name.LocalName; // 移除命名空间声明属性 element.Attributes().Where(a => a.IsNamespaceDeclaration).Remove(); // 移除属性的命名空间前缀 foreach (var attr in element.Attributes().ToList()) { if (attr.Name.Namespace != XNamespace.None) { attr.Remove(); element.Add(new XAttribute(attr.Name.LocalName, attr.Value)); } } } using (var reader = doc.CreateReader()) { var serializer = new XmlSerializer(typeof(Jpk)); return (Jpk)serializer.Deserialize(reader); } }
两种方案都不需要在Jpk类的XmlRoot特性中配置命名空间,保持默认配置即可适配所有来源的同结构XML。
内容的提问来源于stack exchange,提问作者Maurice Klimek
相关产品推荐
相关产品推荐

