XDocument保存删除节点前缀

我有一个XML文档(自行开发),其结构如下:

          UIPTaskId gc8f3715c-4a82-42d2-916c-51515083e7e5   UIPTaskName g359FC555-9CC7-47D4-8ED3-EF973E7D74D7 Responsible Individual   Search? g57201da8-62b4-46f2-9329-c71d86f39ffc True      

我有一个实用程序来清理XML文档,并使用XDocument加载文件,然后循环通过某些节点并替换值。 完成后,我调用Save方法将文件保存在新位置,经过进一步检查,Save方法在每个节点上删除我的wf前缀。 我怎么能保留这个呢? 难道我做错了什么? 以下是我的代码示例:

 string wf = "wf"; string wkfl = "C:\\MyFiles\\Temp\\myfile1.rrr"; XDocument xdoc = XDocument.Load(wkfl); XElement variables= xdoc.Descendents(wf + "variables").Single(); foreach(XElement variable in variables.Elements(wf + "variable")) { XElement name = variable.Element(wf + "name"); name.Value = name.Value + "_MODIFIED"; } xdoc.Save(wkfl.Replace("\\Temp\\", "\\Modified\\")); 

Save方法生成以下XML:

           UIPTaskId gc8f3715c-4a82-42d2-916c-51515083e7e5   UIPTaskName g359FC555-9CC7-47D4-8ED3-EF973E7D74D7 Responsible Individual   Search? g57201da8-62b4-46f2-9329-c71d86f39ffc True      

只需加载XML并再次编写它而不进行任何编辑,就可以重现此行为。 这样做:

  var xdoc = XDocument.Parse(xml); Debug.WriteLine(xdoc.ToXml()); 

产生输出:

      ... 

使用辅助方法:

 public static class XmlSerializationHelper { public static string ToXml(this XDocument xDoc) { using (TextWriter writer = new StringWriter()) { xDoc.Save(writer); return writer.ToString(); } } } 

为什么会这样?

  1. 您有两个具有相同值的命名空间,默认命名空间和带有前缀wf的命名空间:

     xmlns="http://example.com/workflow" xmlns:wf="http://example.com/workflow" 
  2. 因此,前缀wf:wf元素和所有子元素完全没有前缀完全相同

  3. 因此,当将自身写回XML时, XElement可以有效地使用前缀wf:或者根本不使用前缀,而不会改变输出XML的语义。

  4. 那么XElement如何在多个有效前缀之间进行选择? 事实certificate ,从XElement的引用源开始 ,命名空间/前缀属性对在写入时按照添加顺序被推送到下推式堆栈 ,然后从堆栈的顶部到底部检查与元素命名空间的匹配 -以相反的顺序有效地进行匹配,其中添加了属性。

  5. 因此,您的XElements被赋予两个可能的有效前缀中的第二个 – 即没有前缀。

总而言之,带有前缀的XML和没有前缀的XML在语义上是相同的。 没有合适的XML解析器应该关心差异。

然而,如果出于某种原因,您正在使用的某些代码假定使用wf:前缀而不是检查实际的命名空间名称(尽管它不应该),您可以通过将默认命名空间重新排序来强制使用该前缀写出XML。根文档属性列表的开头:

  public static void ReorderDefaultNamespaceToBeginning(XElement xElement) { var attrArray = xElement.Attributes().ToArray(); int defaultIndex = -1; for (int i = 0; i < attrArray.Length && defaultIndex == -1; i++) { var attr = attrArray[i]; if (attr.Name == XName.Get("xmlns", string.Empty)) defaultIndex = i; } if (defaultIndex < 0) return; // No default namespace int firstIndex = -1; for (int i = 0; i < attrArray.Length && firstIndex == -1; i++) { if (i == defaultIndex) continue; var attr = attrArray[i]; if (attr.Name.NamespaceName == "http://www.w3.org/2000/xmlns/" && attr.Value == attrArray[defaultIndex].Value) firstIndex = i; } if (defaultIndex != -1 && firstIndex != -1 && defaultIndex > firstIndex) { foreach (var attr in attrArray) attr.Remove(); attrArray.Swap(defaultIndex, firstIndex); foreach (var attr in attrArray) xElement.Add(attr); } } public static class ListHelper { public static void Swap(this T[] list, int i, int j) { if (i != j) { T temp = list[i]; list[i] = list[j]; list[j] = temp; } } } 

(这利用了未记录的事实,即以与外观相反的顺序检查名称空间前缀。)一旦执行此操作,将返回wf:前缀。