复制XML节点并在其他位置使用,规避XML命名空间验证问题
我明白你现在遇到的问题——从XML文件解析结构时,把无需修改的节点层级存成了字符串,结果转成XmlDocumentFragment附加到新XML时,因为片段里的命名空间前缀(比如mxswa)没声明,直接抛出了XmlException。下面给你两个实用的解决办法,都是针对命名空间前缀映射的问题:
方案1:给目标父节点预先声明所有需要的命名空间
既然你要把片段附加到arguments节点,那最简单的方式就是先给这个父节点添加上所有片段用到的命名空间前缀和对应的URI。这样当片段被追加进去时,解析器就能通过父节点的声明识别这些前缀了。
代码示例:
// 先给arguments节点添加所有必要的命名空间声明(URI要和你原始XML里的一致!) arguments.SetAttribute("xmlns:mxswa", "http://schemas.microsoft.com/xrm/2011/workflowactivities"); arguments.SetAttribute("xmlns:x", "http://schemas.microsoft.com/winfx/2006/xaml"); arguments.SetAttribute("xmlns:scg", "clr-namespace:System.Collections.Generic;assembly=mscorlib"); arguments.SetAttribute("xmlns:mcw", "clr-namespace:Microsoft.Crm.Workflow;assembly=Microsoft.Crm.Workflow"); arguments.SetAttribute("xmlns:mxs", "clr-namespace:Microsoft.Xrm.Sdk;assembly=Microsoft.Xrm.Sdk"); // 再正常创建并加载片段 XmlDocumentFragment fragment = doc.CreateDocumentFragment(); fragment.InnerXml = xmlString; arguments.AppendChild(fragment);
⚠️ 注意:上面的URI是根据CRM工作流相关的常见命名空间推测的,你一定要替换成你原始XML文档里实际使用的命名空间URI,否则还是会出问题。
方案2:用带命名空间的临时根节点包裹片段
如果不想修改arguments节点的命名空间(比如担心污染现有结构),可以先把你的XML字符串包裹在一个包含所有命名空间声明的临时根节点里,加载这个完整的XML后,再把需要的节点提取出来附加到目标位置。
代码示例:
// 构造带全量命名空间声明的临时XML string wrappedXml = $@" <tempRoot xmlns:mxswa=""http://schemas.microsoft.com/xrm/2011/workflowactivities"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:scg=""clr-namespace:System.Collections.Generic;assembly=mscorlib"" xmlns:mcw=""clr-namespace:Microsoft.Crm.Workflow;assembly=Microsoft.Crm.Workflow"" xmlns:mxs=""clr-namespace:Microsoft.Xrm.Sdk;assembly=Microsoft.Xrm.Sdk""> {xmlString} </tempRoot>"; // 加载临时XML文档 XmlDocument tempDoc = new XmlDocument(); tempDoc.LoadXml(wrappedXml); // 提取目标节点并导入到原文档,再附加到arguments XmlNode targetNode = tempDoc.DocumentElement.FirstChild; arguments.AppendChild(doc.ImportNode(targetNode, true));
这个方法相当于给片段临时提供了一个有命名空间上下文的环境,解析完成后再把核心节点移到目标位置,不会影响原节点的命名空间结构。
补充说明:为什么会出现这个错误?
XML里的前缀(比如mxswa)本身没有意义,必须和对应的命名空间URI绑定才能被解析器识别。而XmlDocumentFragment本身是脱离节点上下文的,它自己没有任何命名空间声明,所以解析器不知道mxswa对应的是什么URI,自然就抛出了未声明前缀的错误。只要给片段提供一个包含这些前缀声明的上下文(父节点或者临时根节点),问题就能解决。
内容的提问来源于stack exchange,提问作者Mohfath

