使用C#向现有XML文档插入XML字符串出现异常求助
问题
使用C#的System.Xml类操作现有XML文档时,需要将字符串形式的XML片段插入到指定节点中。当前实现方式是创建新元素后设置其InnerXml属性,但插入的子节点自动添加了空的xmlns=""属性,导致后续用命名空间查询节点时返回null。
当前代码:
using System.Xml; string nsUri = "http://myorg/myns"; string baseDocument = @$"<?xml version=""1.0"" encoding=""utf-16""?><Root xmlns=""{nsUri}""></Root>"; XmlDocument doc = new XmlDocument(); doc.LoadXml(baseDocument); var nsm = new XmlNamespaceManager(doc.NameTable); nsm.AddNamespace("default", nsUri); var target = doc.CreateElement("Target", nsUri); target.InnerXml= @" <Sub1>This is a subnode</Sub1> <Sub2>Here is another subnode</Sub2> "; doc.DocumentElement.AppendChild(target); doc.Save(@"C:\text.xml");
输出的XML结果:
<?xml version="1.0" encoding="utf-16"?> <Root xmlns="http://myorg/myns"> <Target> <Sub1 xmlns="">This is a subnode</Sub1> <Sub2 xmlns="">Here is another subnode</Sub2> </Target> </Root>
执行以下查询代码时,sub1返回null:
var sub1 = target.SelectSingleNode("default:Sub1", nsm);
备注:
- 已知可以逐个创建元素,但XML片段过长,手动创建过于繁琐。
- 尝试过
XmlDocumentFragment,结果相同。
解决方案
问题核心是插入的XML片段没有继承父节点的默认命名空间,导致解析时被归到空命名空间下,和查询时使用的命名空间不匹配。以下是两种可行的解决方式:
方式一:给XML片段显式添加默认命名空间
直接在XML片段的子节点中声明默认命名空间,让它们和父节点使用同一命名空间:
var target = doc.CreateElement("Target", nsUri); // 给每个子节点添加目标命名空间声明 target.InnerXml = @" <Sub1 xmlns=""http://myorg/myns"">This is a subnode</Sub1> <Sub2 xmlns=""http://myorg/myns"">Here is another subnode</Sub2> ";
这种方式简单直接,适合片段结构不复杂的场景。
方式二:加载片段后批量修改节点的命名空间
如果不想修改原始XML片段字符串,可以先将片段加载到临时XmlDocument中,遍历所有节点并替换命名空间,再导入到目标文档:
using System.Xml; string nsUri = "http://myorg/myns"; string baseDocument = @$"<?xml version=""1.0"" encoding=""utf-16""?><Root xmlns=""{nsUri}""></Root>"; XmlDocument doc = new XmlDocument(); doc.LoadXml(baseDocument); var nsm = new XmlNamespaceManager(doc.NameTable); nsm.AddNamespace("default", nsUri); var target = doc.CreateElement("Target", nsUri); doc.DocumentElement.AppendChild(target); // 处理XML片段 string fragmentXml = @" <Sub1>This is a subnode</Sub1> <Sub2>Here is another subnode</Sub2> "; XmlDocument tempDoc = new XmlDocument(); tempDoc.LoadXml($"<Temp>{fragmentXml}</Temp>"); // 用临时节点包裹片段 // 遍历临时节点的子节点,修改命名空间并添加到目标节点 foreach (XmlNode node in tempDoc.DocumentElement.ChildNodes) { XmlNode importedNode = doc.ImportNode(node, true); importedNode.NamespaceURI = nsUri; // 移除自动生成的空xmlns属性 if (importedNode.Attributes?["xmlns"] != null) { importedNode.Attributes.RemoveNamedItem("xmlns"); } target.AppendChild(importedNode); } doc.Save(@"C:\text.xml");
执行后输出的XML将不再有空xmlns属性,且SelectSingleNode("default:Sub1", nsm)能正确返回节点。
关于XmlDocumentFragment的说明
XmlDocumentFragment本身不会自动继承父节点的命名空间,解析时仍会按照片段自身的命名空间处理。所以需要结合上述两种方式:要么给片段添加命名空间声明,要么导入后修改节点的命名空间,才能解决问题。
内容的提问来源于stack exchange,提问作者Master_T
相关产品推荐
相关产品推荐

