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>

person magol    schedule 02.12.2010    source источник


Ответы (2)


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

person Martin Peck    schedule 02.12.2010
comment
Я это понимаю, но как мне это сделать? Я пробовал defaultSettings.Name = ns+ defaultSettings.Name.LocalName; Но я должен сделать это для всех подэлементов. Должно быть что-то лучше. - person magol; 03.12.2010
comment
Вам нужно либо преобразовать документ с помощью методов Xslt, либо прочитать каждый элемент и преобразовать его в код. По сути, XDocument, который вы загрузили, знает пространство имен каждого элемента в этом документе и знает, что это не то же самое пространство имен, что и foo. - person Martin Peck; 03.12.2010
comment
Могу ли я изменить XML-файл, из которого я читаю, чтобы он находился в правильном имени? - person magol; 03.12.2010
comment
У вас есть фрагмент/образец ваших настроек xml? - person Martin Peck; 03.12.2010
comment
Кроме того, ваш пример кода не компилируется. Вы удалили некоторые аспекты этого? 2-я строка выглядит неправильно. - person Martin Peck; 03.12.2010
comment
@magol Правильно ли я думаю, что вас не волнуют пространства имен файла xml? Вы просто хотите, чтобы эти элементы отображались вместе с вашим выходным документом в пространстве имен этого выходного документа? - person Martin Peck; 04.12.2010
comment
@Martin Peck - это именно то, что мне нужно. Мне удалось решить это до того, как вы ответили, рекурсивно пройдясь по всем элементам, но использовать DescendantsAndSelf было намного умнее. Спасибо за ответ. - person magol; 09.12.2010

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

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;
}
person Anapsidae    schedule 12.02.2018