使用LINQ-to-XML如何避免Descendants返回冗余命名空间并保留前缀
解决方法:保留前缀但去除重复命名空间声明的XML节点获取
方案1:使用XmlWriter控制命名空间输出
LINQ to XML的XElement.ToString()默认会为节点添加必要的命名空间声明,哪怕上层已经存在。通过XmlWriterSettings的NamespaceHandling.OmitDuplicates配置,可以省略重复的命名空间声明,输出和原XML一致的片段:
string soapEnvelopeXml = @"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:gdp=""http://example.com/asdf""> <soapenv:Header/> <soapenv:Body> <gdp:GetLotNum/> </soapenv:Body> </soapenv:Envelope>"; XElement envelope = XElement.Parse(soapEnvelopeXml); XNamespace envelopeNs = "http://schemas.xmlsoap.org/soap/envelope/"; var bodyNode = envelope.Descendants(envelopeNs + "Body").FirstOrDefault(); if (bodyNode == null) return null; var settings = new XmlWriterSettings { OmitXmlDeclaration = true, NamespaceHandling = NamespaceHandling.OmitDuplicates, Indent = true, IndentChars = " " // 匹配原XML的缩进格式 }; using (var sw = new StringWriter()) using (var writer = XmlWriter.Create(sw, settings)) { bodyNode.WriteTo(writer); return sw.ToString(); }
原理说明
NamespaceHandling.OmitDuplicates会让XmlWriter自动忽略那些已经在父级节点(或更上层)声明过的命名空间,因此输出的<soapenv:Body>节点不会重复添加xmlns:soapenv和xmlns:gdp声明,仅保留前缀和节点结构。
方案2:使用XmlReader直接读取原始XML片段
如果需要完全匹配原XML的格式(包括空格、缩进),可以用XmlReader定位目标节点后,直接读取其原始XML文本:
string soapEnvelopeXml = @"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:gdp=""http://example.com/asdf""> <soapenv:Header/> <soapenv:Body> <gdp:GetLotNum/> </soapenv:Body> </soapenv:Envelope>"; using (var reader = XmlReader.Create(new StringReader(soapEnvelopeXml))) { while (reader.Read()) { // 匹配目标节点:命名空间+本地名 if (reader.NodeType == XmlNodeType.Element && reader.NamespaceURI == "http://schemas.xmlsoap.org/soap/envelope/" && reader.LocalName == "Body") { // 读取节点的原始外部XML return reader.ReadOuterXml(); } } } return null;
原理说明
ReadOuterXml()直接从输入的XML字符串中截取目标节点的原始文本,完全保留原有的前缀、格式和缩进,不会生成新的命名空间声明。
内容的提问来源于stack exchange,提问作者Patrick Szalapski
相关产品推荐
相关产品推荐

