XDocument保存删除节点前缀 [英] XDocument Save Removing Node Prefixes

查看:77
本文介绍了XDocument保存删除节点前缀的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an XML document (homegrown) that has a structure like the following:

<?xml version="1.0" encoding="utf-8"?>
    <wf:wf version="1.0a" xmlns:wf="http://example.com/workflow" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://example.com/workflow">
  <wf:assemblies />
  <wf:dataDefinitions />
  <wf:processes />
  <wf:workflows>
    <wf:workflow id="g08615517-cdfd-4091-a053-217a965f7118">
      <wf:arguments />
      <wf:variables>
        <wf:variable id="g39ffecc9-f570-41c1-9ee0-b9358d63da3c" parameterType="Hidden">
          <wf:name>UIPTaskId</wf:name>
          <wf:dataDefinitionId>gc8f3715c-4a82-42d2-916c-51515083e7e5</wf:dataDefinitionId>
        </wf:variable>
        <wf:variable id="g46663a0c-7e60-4bd2-80df-16cd544087ad" parameterType="Hidden">
          <wf:name>UIPTaskName</wf:name>
          <wf:dataDefinitionId>g359FC555-9CC7-47D4-8ED3-EF973E7D74D7</wf:dataDefinitionId>
          <wf:value>Responsible Individual</wf:value>
        </wf:variable>
        <wf:variable id="gb32914d5-6761-4e82-b571-c8944a796fd9" parameterType="Hidden">
          <wf:name>Search?</wf:name>
          <wf:dataDefinitionId>g57201da8-62b4-46f2-9329-c71d86f39ffc</wf:dataDefinitionId>
          <wf:value>True</wf:value>
        </wf:variable>
    </wf:variables>
</wf:workflow>
</wf:workflows>
</wf:wf>

我有一个实用程序来清理XML文档,并且正在使用XDocument加载文件,然后遍历某些节点并替换值.完成后,我将调用Save方法将文件保存到新位置,并且在进一步检查后,Save方法将删除每个节点上的wf前缀.我该如何保存呢?难道我做错了什么?这是我的代码示例:

I have a utility to clean up the XML documents and am using XDocument to load the file and then loop through certain nodes and replace values. Once done, I am calling the Save method to save the file in a new location and upon further inspection, the Save method is removing my wf prefix on every node. How can I preserve this? Am I doing something wrong? Here is a sample of my code:

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:

The Save method produces the following XML:

<?xml version="1.0" encoding="utf-8"?>
        <wf version="1.0a" xmlns:wf="http://example.com/workflow" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://example.com/workflow">
      <assemblies />
      <dataDefinitions />
      <processes />
      <workflows>
        <workflow id="g08615517-cdfd-4091-a053-217a965f7118">
          <arguments />
          <variables>
            <variable id="g39ffecc9-f570-41c1-9ee0-b9358d63da3c" parameterType="Hidden">
              <name>UIPTaskId</name>
              <dataDefinitionId>gc8f3715c-4a82-42d2-916c-51515083e7e5</dataDefinitionId>
            </variable>
            <variable id="g46663a0c-7e60-4bd2-80df-16cd544087ad" parameterType="Hidden">
              <name>UIPTaskName</name>
              <dataDefinitionId>g359FC555-9CC7-47D4-8ED3-EF973E7D74D7</dataDefinitionId>
              <value>Responsible Individual</value>
            </variable>
            <variable id="gb32914d5-6761-4e82-b571-c8944a796fd9" parameterType="Hidden">
              <name>Search?</name>
              <dataDefinitionId>g57201da8-62b4-46f2-9329-c71d86f39ffc</dataDefinitionId>
              <value>True</value>
            </variable>
        </variables>
    </workflow>
    </workflows>
    </wf>

推荐答案

可以简单地通过加载XML并重新编写而不进行任何编辑来重现此行为.正在执行:

This behavior can be reproduced simply by loading your XML and writing it again without making any edits. Doing:

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

产生输出:

<wf version="1.0a" xmlns:wf="http://example.com/workflow" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://example.com/workflow">
  <assemblies />
  <dataDefinitions />
  <processes />
  <workflows>
      ...

使用辅助方法:

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

为什么会这样?

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

  1. You have two namespaces with identical values, the default namespace and the namespace with prefix wf:

xmlns="http://example.com/workflow"
xmlns:wf="http://example.com/workflow"

  • 因此,前缀wf:表示完全相同的东西,因为对于wf元素和所有子元素根本没有前缀.

  • Thus the prefix wf: means exactly the same thing as having no prefix at all for the wf element and all child elements.

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

    Thus, when writing itself back to XML, XElement could validly use the prefix wf:, or use no prefix at all, without changing the semantic meaning of the output XML.

    那么XElement如何在多个有效前缀之间进行选择? 事实证明,来自

    So how does XElement chose between multiple valid prefixes? As it turns out, from the reference source for XElement, the namespace/prefix attribute pairs are pushed onto a push-down stack in order of addition while writing, then checked for matches against the element namespace from top to bottom of the stack -- effectively doing the match in reverse order in which the attributes are added.

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

    Thus your XElements are given the second of the two possible valid prefixes -- namely no prefix.

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

    To summarize, the XML with the prefixes and the XML without the prefixes are semantically identical. No proper XML parser should care about the difference.

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

    Nevertheless, if for whatever reason some code you are working with assumes the wf: prefix rather than checking the actual namespace name (though it shouldn't), you can force your XML to be written out with that prefix by reordering the default namespace to the beginning of the root document attribute list:

        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<T>(this T[] list, int i, int j)
        {
            if (i != j)
            {
                T temp = list[i];
                list[i] = list[j];
                list[j] = temp;
            }
        }
    }
    

    (这利用了未公开文件的事实,即名称空间前缀以相反的顺序检查.)一旦执行此操作,就会返回wf:前缀.

    (This takes advantage of the undocumented fact that namespace prefixes are checked in reverse order of appearance.) Once you do this the wf: prefixes will return.

    这篇关于XDocument保存删除节点前缀的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆