使用XmlReader提取XML元素时避免子元素重复添加xmlns命名空间的问题
我明白你的问题了——用XmlReader流式处理大XML文件时,提取的Header、Reference这些子元素被自动加上了重复的xmlns命名空间,明明根元素Data已经声明过了,看着特别冗余对吧?
问题原因
问题出在你创建XmlReader时没有开启命名空间感知。默认情况下,XmlReader的NamespaceAware属性是false,这意味着它不会识别XML中的默认命名空间,读取出来的Header、Reference等元素都是不带命名空间的。当你把这些“无命名空间”的元素添加到带有命名空间的Data根元素下时,XElement为了保证元素的命名空间正确,就会自动给每个子元素加上xmlns声明,导致重复。
解决方案
方法一:开启XmlReader的命名空间感知(推荐)
修改你的XmlReader创建代码,添加XmlReaderSettings并设置NamespaceAware = true,这样读取的元素会自动继承根元素的命名空间,添加到新Data元素下时就不会重复声明了:
XmlReaderSettings settings = new XmlReaderSettings(); settings.NamespaceAware = true; // 关键:开启命名空间感知 using (XmlReader reader = XmlReader.Create(xmlFile, settings)) { reader.MoveToContent(); while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && reader.Name == "Header") { hdrval = XElement.Load(reader.ReadSubtree()); } if (reader.NodeType == XmlNodeType.Element && reader.Name == "Reference") { refval = XElement.Load(reader.ReadSubtree()); } if (reader.NodeType == XmlNodeType.Element && reader.Name == "Sender") { sndval = XElement.Load(reader.ReadSubtree()); } if (reader.NodeType == XmlNodeType.Element && reader.Name == "Item") { break; } } } XNamespace ns = "http://foo.schema"; XElement Header = new(ns + "Data", hdrval, refval, sndval, );
这样处理后,生成的XML就只会在根Data元素上显示xmlns声明,子元素不会重复出现。
方法二:手动为读取的元素添加命名空间(备选)
如果你因为某些原因不能修改XmlReader的设置,也可以在读取子元素后,重新创建带有正确命名空间的元素,把原来的节点内容移过去:
XNamespace ns = "http://foo.schema"; // 读取Header时替换成: if (reader.NodeType == XmlNodeType.Element && reader.Name == "Header") { var temp = XElement.Load(reader.ReadSubtree()); hdrval = new XElement(ns + "Header", temp.Nodes()); } // Reference和Sender同理: if (reader.NodeType == XmlNodeType.Element && reader.Name == "Reference") { var temp = XElement.Load(reader.ReadSubtree()); refval = new XElement(ns + "Reference", temp.Nodes()); } if (reader.NodeType == XmlNodeType.Element && reader.Name == "Sender") { var temp = XElement.Load(reader.ReadSubtree()); sndval = new XElement(ns + "Sender", temp.Nodes()); }
这种方法相当于手动给元素补上正确的命名空间,避免自动添加重复的xmlns声明。
补充说明
为什么用LINQ to XML直接加载整个文件没问题?因为XElement默认是命名空间感知的,它会正确解析根元素的默认命名空间并应用到所有子元素上,所以不会出现重复声明的情况。但对于大文件来说,流式读取的方式确实更节省内存,所以第一种开启命名空间感知的方法是最优解。
备注:内容来源于stack exchange,提问作者JaggenSWE

