XDocument 保存删除节点前缀
XDocument Save Removing Node Prefixes
我有一个 XML 文档(自制的),其结构如下:
<?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 前缀。我怎样才能保存这个?难道我做错了什么?这是我的代码示例:
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:
<?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 并在不进行任何编辑的情况下再次写入,即可重现此行为。正在做:
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();
}
}
}
为什么会这样?
您有两个具有 相同 值的命名空间,默认命名空间和前缀为 wf
:
的命名空间
xmlns="http://example.com/workflow"
xmlns:wf="http://example.com/workflow"
因此前缀 wf:
的意思 与 wf
元素和所有子元素完全没有前缀相同 元素.
因此,当将自身写回 XML 时,XElement
可以有效地使用前缀 wf:
,或者根本不使用前缀,而不会改变语义输出的含义 XML.
那么XElement
如何在多个有效前缀之间进行选择呢?堆栈的 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——有效地以添加属性的相反顺序进行匹配。
因此您的 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<T>(this T[] list, int i, int j)
{
if (i != j)
{
T temp = list[i];
list[i] = list[j];
list[j] = temp;
}
}
}
(这利用了 未记录的 事实,即名称空间前缀按出现的相反顺序进行检查。)一旦你这样做, wf:
前缀将 return.
我有一个 XML 文档(自制的),其结构如下:
<?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 前缀。我怎样才能保存这个?难道我做错了什么?这是我的代码示例:
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:
<?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 并在不进行任何编辑的情况下再次写入,即可重现此行为。正在做:
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();
}
}
}
为什么会这样?
您有两个具有 相同 值的命名空间,默认命名空间和前缀为
的命名空间wf
:xmlns="http://example.com/workflow" xmlns:wf="http://example.com/workflow"
因此前缀
wf:
的意思 与wf
元素和所有子元素完全没有前缀相同 元素.因此,当将自身写回 XML 时,
XElement
可以有效地使用前缀wf:
,或者根本不使用前缀,而不会改变语义输出的含义 XML.那么
XElement
如何在多个有效前缀之间进行选择呢?堆栈的 As it turns out, from the reference source forXElement
, 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——有效地以添加属性的相反顺序进行匹配。因此您的
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<T>(this T[] list, int i, int j)
{
if (i != j)
{
T temp = list[i];
list[i] = list[j];
list[j] = temp;
}
}
}
(这利用了 未记录的 事实,即名称空间前缀按出现的相反顺序进行检查。)一旦你这样做, wf:
前缀将 return.