Как установить пространство имен XML по умолчанию для XDocument - PullRequest
44 голосов
/ 20 мая 2010

Как установить пространство имен по умолчанию для существующего XDocument (чтобы я мог десериализовать его с помощью DataContractSerializer). Я попробовал следующее:

var doc = XDocument.Parse("<widget/>");
var attrib = new XAttribute("xmlns",
                            "http://schemas.datacontract.org/2004/07/Widgets");
doc.Root.Add(attrib);

Исключение, которое я получаю: The prefix '' cannot be redefined from '' to 'http://schemas.datacontract.org/2004/07/Widgets' within the same start element tag.

Есть идеи?

Ответы [ 6 ]

49 голосов
/ 22 февраля 2011

Не уверен, что это уже работало в .net 3.5 или только в 4, но у меня это нормально работает:

XNamespace ns = @"http://mynamespace";
var result = new XDocument(
    new XElement(ns + "rootNode",
        new XElement(ns + "child",
            new XText("Hello World!")
         )
     )
 );

производит этот документ:

<rootNode xmlns="http://mynamespace">
    <child>Hello World!</child>
</rootNode>

Важно всегда использовать синтаксис ns + "NodeName".

47 голосов
/ 20 мая 2010

Похоже, что Linq to XML не предоставляет API для этого варианта использования (отказ от ответственности: я не очень глубоко исследовал). Если изменить пространство имен корневого элемента, например:

XNamespace xmlns = "http://schemas.datacontract.org/2004/07/Widgets";
doc.Root.Name = xmlns + doc.Root.Name.LocalName;

Только корневому элементу будет изменено его пространство имен. У всех детей будет явный пустой тег xmlns.

Решение может быть что-то вроде этого:

public static void SetDefaultXmlNamespace(this XElement xelem, XNamespace xmlns)
{
    if(xelem.Name.NamespaceName == string.Empty)
        xelem.Name = xmlns + xelem.Name.LocalName;
    foreach(var e in xelem.Elements())
        e.SetDefaultXmlNamespace(xmlns);
}

// ...
doc.Root.SetDefaultXmlNamespace("http://schemas.datacontract.org/2004/07/Widgets");

Или, если вы предпочитаете версию, которая не изменяет существующий документ:

public XElement WithDefaultXmlNamespace(this XElement xelem, XNamespace xmlns)
{
    XName name;
    if(xelem.Name.NamespaceName == string.Empty)
        name = xmlns + xelem.Name.LocalName;
    else
        name = xelem.Name;
    return new XElement(name,
                    from e in xelem.Elements()
                    select e.WithDefaultXmlNamespace(xmlns));
}
6 голосов
/ 02 декабря 2011

У меня было такое же требование, но я придумал что-то незначительное:

/// <summary>
/// Sets the default XML namespace of this System.Xml.Linq.XElement
/// and all its descendants
/// </summary>
public static void SetDefaultNamespace(this XElement element, XNamespace newXmlns)
{
    var currentXmlns = element.GetDefaultNamespace();
    if (currentXmlns == newXmlns)
        return;

    foreach (var descendant in element.DescendantsAndSelf()
        .Where(e => e.Name.Namespace == currentXmlns)) //!important
    {
        descendant.Name = newXmlns.GetName(descendant.Name.LocalName);
    }
}

Если вы хотите сделать это правильно, вы должны учитывать, что ваш элемент может содержать элементы расширения различных пространств имен. Вы не хотите изменять их все, а только те элементы пространства имен по умолчанию.

3 голосов
/ 25 февраля 2013

R. Ответ Martinho Fernandes выше (который не изменяет существующий документ) просто требует небольшой настройки, чтобы значения элементов также возвращались. Я не проверял это в angst, просто играл с linqpad, извините, юнит-тесты не предусмотрены.

public static XElement SetNamespace(this XElement src, XNamespace ns)
{
    var name = src.isEmptyNamespace() ? ns + src.Name.LocalName : src.Name;
    var element = new XElement(name, src.Attributes(), 
          from e in src.Elements() select e.SetNamespace(ns));
    if (!src.HasElements) element.Value = src.Value;
    return element;
}

public static bool isEmptyNamespace(this XElement src)
{
    return (string.IsNullOrEmpty(src.Name.NamespaceName));
}
2 голосов
/ 14 марта 2013

Модифицированный метод расширения для включения XElement.Value (т.е. конечных узлов):

public static XElement WithDefaultXmlNamespace(this XElement xelem, XNamespace xmlns)
{
    XName name;
    if (xelem.Name.NamespaceName == string.Empty)
        name = xmlns + xelem.Name.LocalName;
    else
        name = xelem.Name;
    if (xelem.Elements().Count() == 0)
    {
        return new XElement(name, xelem.Value);
    }
    return new XElement(name,
                    from e in xelem.Elements()
                    select e.WithDefaultXmlNamespace(xmlns));
}

И теперь он работает для меня!

0 голосов
/ 14 апреля 2015

не забудьте также скопировать оставшиеся атрибуты:

  public static XElement WithDefaultXmlNamespace(this XElement xelem, XNamespace xmlns)
    {
        XName name;
        if (xelem.Name.NamespaceName == string.Empty)
            name = xmlns + xelem.Name.LocalName;
        else
            name = xelem.Name;


        XElement retelement;
        if (!xelem.Elements().Any())
        {
            retelement = new XElement(name, xelem.Value);
        }
        else
         retelement= new XElement(name,
            from e in xelem.Elements()
            select e.WithDefaultXmlNamespace(xmlns));

        foreach (var at in xelem.Attributes())
        {
            retelement.Add(at);
        }

        return retelement;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...