问题 XElement添加了一个xmlns


我正在使用Linq to XML来创建一个新的XML文件。我从现有的XML文件中获取文件的某些部分。我使用以下代码。

var v2 = new XDocument(
  new XDeclaration("1.0", "utf-16", ""),
  new XComment(string.Format("Converted from version 1. Date: {0}", DateTime.Now)),
  new XElement(ns + "keyem",
    new XAttribute(XNamespace.Xmlns + "xsd", xsd.NamespaceName),
    new XAttribute(XNamespace.Xmlns + "xsi", xsi.NamespaceName),
    new XAttribute(xsi + "schemaLocation", schemaLocation.NamespaceName),
    new XAttribute("version", "2"),
    new XAttribute("description", description),
    new XElement(ns + "layout",
      new XAttribute("type", type),
      new XAttribute("height", height),
      new XAttribute("width", width),
      settings.Root)       // XML from an existing file

问题是它添加了xmlns =“”现有文件中的第一个元素。

结果是:

<?xml version="1.0" encoding="utf-16"?>
<foo 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://tempuri.org/KeyEmFileSchema.xsd KeyEmFileSchema.xsd"
  xmlns="http://tempuri.org/KeyEmFileSchema.xsd">
  <settings xmlns="">
      ...
  </settings>
</foo>

我正在阅读的XML文件看起来像这样,但如果需要我可以更改它

<?xml version="1.0" encoding="utf-16"?>
<settings>
  <colormaps>
    <colormap color="Gray"     textcolor="Black"/>
    <colormap color="DarkGray" textcolor="White"/>
    <colormap color="Black"    textcolor="White"/>
    <colormap color="Cyan"     textcolor="Black"/>
  </colormaps>
  <macromaps>
    <macromap pattern="^@([0-9A-F]{2})\|([0-9A-F]{2})$"  replace="{ESC}$1{ESC}$2{MOUSERESET}"/>
    <macromap pattern="^\$([0-9A-F]{2})\|([0-9A-F]{2})$" replace="{USERCLICK}{ESC}$1{ESC}$2{MOUSERESET}"/>
    <macromap pattern="^\$([0-9A-F]{2})$"                replace="{USERCLICK}{ESC}$1"/>
  </macromaps>
  <keydefault color="Cyan"/>
  <groupdefault color="DarkGray"/>
</settings>

6981
2017-12-02 21:24


起源



答案:


您看到了这一点,因为settings元素(可能来自您的文档)不在此命名空间中。它位于default / null-uri命名空间中。

您需要转换输入文档才能更改其命名空间。

这个稍微简化的示例将您的xml文件放入另一个文档中,但在此之前,它会将该xml文件中每个元素的命名空间更改为目标文档的命名空间...

    static void ProcessXmlFile()
    {
        XNamespace ns = "http://tempuri.org/KeyEmFileSchema.xsd/";

        // load the xml document
        XElement settings = XElement.Load("data.xml");

        // shift ALL elements in the settings document into the target namespace
        foreach (XElement e in settings.DescendantsAndSelf())
        {
            e.Name = ns + e.Name.LocalName;
        }

        // write the output document
        var file = new XDocument(new XElement(ns + "foo",
                                        settings));

        Console.Write(file.ToString());            
    }

其结果......

<foo xmlns="http://tempuri.org/KeyEmFileSchema.xsd/">
  <settings>
    <colormaps>
      <colormap color="Gray" textcolor="Black" />
      <colormap color="DarkGray" textcolor="White" />
      <colormap color="Black" textcolor="White" />
      <colormap color="Cyan" textcolor="Black" />
    </colormaps>
    <macromaps>
      <macromap pattern="^@([0-9A-F]{2})\|([0-9A-F]{2})$" replace="{ESC}$1{ESC}$2{MOUSERESET}" />
      <macromap pattern="^\$([0-9A-F]{2})\|([0-9A-F]{2})$" replace="{USERCLICK}{ESC}$1{ESC}$2{MOUSERESET}" />
      <macromap pattern="^\$([0-9A-F]{2})$" replace="{USERCLICK}{ESC}$1" />
    </macromaps>
    <keydefault color="Cyan" />
    <groupdefault color="DarkGray" />
  </settings>
</foo>

如您所见,settings元素现在与foo元素位于同一名称空间中。这本质上是一个快速而又脏的xml转换,显然它不尊重您导入的xml doc中的任何命名空间。但这可能是你所追求的,或者至少可能构成更强大的基础。


11
2017-12-02 21:30



我理解,但我该怎么做?我试过defaultSettings.Name = ns + defaultSettings.Name.LocalName;但我必须为所有子元素做这件事。必须是更好的东西。 - magol
您需要使用Xslt技术转换文档,或者读取每个元素并在代码中转换它。基本上,您加载的XDocument知道该文档中每个元素的命名空间,并且知道它与foo不是同一个命名空间。 - Martin Peck
我可以更改我读取的xml文件,使其成为正确的名称吗? - magol
你有你的设置xml的片段/样本吗? - Martin Peck
此外,您的代码示例不编译。你有没有删除它的一些方面?第二行看起来不正确。 - Martin Peck


答案:


您看到了这一点,因为settings元素(可能来自您的文档)不在此命名空间中。它位于default / null-uri命名空间中。

您需要转换输入文档才能更改其命名空间。

这个稍微简化的示例将您的xml文件放入另一个文档中,但在此之前,它会将该xml文件中每个元素的命名空间更改为目标文档的命名空间...

    static void ProcessXmlFile()
    {
        XNamespace ns = "http://tempuri.org/KeyEmFileSchema.xsd/";

        // load the xml document
        XElement settings = XElement.Load("data.xml");

        // shift ALL elements in the settings document into the target namespace
        foreach (XElement e in settings.DescendantsAndSelf())
        {
            e.Name = ns + e.Name.LocalName;
        }

        // write the output document
        var file = new XDocument(new XElement(ns + "foo",
                                        settings));

        Console.Write(file.ToString());            
    }

其结果......

<foo xmlns="http://tempuri.org/KeyEmFileSchema.xsd/">
  <settings>
    <colormaps>
      <colormap color="Gray" textcolor="Black" />
      <colormap color="DarkGray" textcolor="White" />
      <colormap color="Black" textcolor="White" />
      <colormap color="Cyan" textcolor="Black" />
    </colormaps>
    <macromaps>
      <macromap pattern="^@([0-9A-F]{2})\|([0-9A-F]{2})$" replace="{ESC}$1{ESC}$2{MOUSERESET}" />
      <macromap pattern="^\$([0-9A-F]{2})\|([0-9A-F]{2})$" replace="{USERCLICK}{ESC}$1{ESC}$2{MOUSERESET}" />
      <macromap pattern="^\$([0-9A-F]{2})$" replace="{USERCLICK}{ESC}$1" />
    </macromaps>
    <keydefault color="Cyan" />
    <groupdefault color="DarkGray" />
  </settings>
</foo>

如您所见,settings元素现在与foo元素位于同一名称空间中。这本质上是一个快速而又脏的xml转换,显然它不尊重您导入的xml doc中的任何命名空间。但这可能是你所追求的,或者至少可能构成更强大的基础。


11
2017-12-02 21:30



我理解,但我该怎么做?我试过defaultSettings.Name = ns + defaultSettings.Name.LocalName;但我必须为所有子元素做这件事。必须是更好的东西。 - magol
您需要使用Xslt技术转换文档,或者读取每个元素并在代码中转换它。基本上,您加载的XDocument知道该文档中每个元素的命名空间,并且知道它与foo不是同一个命名空间。 - Martin Peck
我可以更改我读取的xml文件,使其成为正确的名称吗? - magol
你有你的设置xml的片段/样本吗? - Martin Peck
此外,您的代码示例不编译。你有没有删除它的一些方面?第二行看起来不正确。 - Martin Peck


你可以为此编写一个扩展方法。 此方法具有返回值,因此它支持链接,但也更改原始的转换,因此可以在不分配的情况下使用它。

public static XElement EnsureNamespaceExists(this XElement xElement, XNamespace xNamespace)
{
    string nodeName = xElement.Name.LocalName;

    if (!xElement.HasAttribute("xmlns"))
    {
        foreach (XElement tmpElement in xElement.DescendantsAndSelf())
        {
            tmpElement.Name = xNamespace + tmpElement.Name.LocalName;
        }
        xElement = new XElement(xNamespace + nodeName, xElement.FirstNode);
    }

    return xElement;
}

1
2018-02-12 12:25