XElement добавляет xmlns

Я использую Linq для 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>

Ответ 1

Вы видите это, потому что элемент настроек (предположительно исходящий из вашего документа) не живет в этом пространстве имен. Он живет в пространстве имен по умолчанию /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>

Как вы можете видеть, элемент настроек теперь находится в том же пространстве имен, что и элемент foo. Это, по сути, быстрое и грязное преобразование xml, и, очевидно, оно не учитывает пространства имен в XML-документе, который вы импортируете. Но это может быть то, что вам нужно, или, по крайней мере, может стать основой чего-то более надежного.

Ответ 2

Вы можете написать для этого метод расширения. Этот метод имеет возвращаемое значение, поэтому поддерживает цепочку, но также изменяет преобразование оригинала, поэтому его можно использовать без назначения.

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;
}