Как удалить атрибут xmlns node, кроме root, в XDocument

Ситуация

Я использую XDocument, чтобы попытаться удалить атрибут xmlns="" на первом внутреннем node:

<Root xmlns="http://my.namespace">
    <Firstelement xmlns="">
        <RestOfTheDocument />
    </Firstelement>
</Root>

Итак, в результате я хочу:

<Root xmlns="http://my.namespace">
    <Firstelement>
        <RestOfTheDocument />
    </Firstelement>
</Root>

Код

doc = XDocument.Load(XmlReader.Create(inStream));

XElement inner = doc.XPathSelectElement("/*/*[1]");
if (inner != null)
{
    inner.Attribute("xmlns").Remove();
}

MemoryStream outStream = new MemoryStream();
XmlWriter writer = XmlWriter.Create(outStream);
doc.Save(writer); // <--- Exception occurs here

Проблема

При попытке сохранить документ я получаю следующее исключение:

Префикс '' не может быть переопределен с '' на 'http://my.namespace' в пределах одного и того же тега элемента запуска.

Что это значит и что я могу сделать, чтобы удалить этот pesky xmlns=""?

Примечания

  • Я хочу сохранить корневое пространство имен node
  • Я хочу, чтобы только этот xmlns удалялся, в документе не будет других атрибутов xmlns.

Update

Я пробовал использовать код, вдохновленный ответами на этот вопрос:

inner = new XElement(inner.Name.LocalName, inner.Elements());

При отладке атрибут xmlns исчезает, но я получаю то же исключение.

Ответы

Ответ 1

Я думаю, что код ниже - это то, что вы хотите. Вам нужно поместить каждый элемент в правильное пространство имен и удалить любые атрибуты xmlns='' для затронутых элементов. Последняя часть требуется, так как иначе LINQ to XML в основном пытается оставить вас с элементом

<!-- This would be invalid -->
<Firstelement xmlns="" xmlns="http://my.namespace">

Здесь код:

using System;
using System.Xml.Linq;

class Test
{
    static void Main()
    {
        XDocument doc = XDocument.Load("test.xml");
        // All elements with an empty namespace...
        foreach (var node in doc.Root.Descendants()
                                .Where(n => n.Name.NamespaceName == ""))
        {
             // Remove the xmlns='' attribute. Note the use of
             // Attributes rather than Attribute, in case the
             // attribute doesn't exist (which it might not if we'd
             // created the document "manually" instead of loading
             // it from a file.)
             node.Attributes("xmlns").Remove();
             // Inherit the parent namespace instead
             node.Name = node.Parent.Name.Namespace + node.Name.LocalName;
        }
        Console.WriteLine(doc); // Or doc.Save(...)
    }
}

Ответ 2

Нет необходимости "удалять" пустой атрибут xmlns. Вся причина, по которой добавляется пустой атрибут xmlns, заключается в том, что пространство имен ваших дочерних узлов пуст (= '') и поэтому отличается от вашего корня node. Добавление того же пространства имен к вашим дочерним элементам также решит этот "побочный эффект".

XNamespace xmlns = XNamespace.Get("http://my.namespace");

// wrong
var doc = new XElement(xmlns + "Root", new XElement("Firstelement"));

// gives:
<Root xmlns="http://my.namespace">
    <Firstelement xmlns="" />
</Root>

// right
var doc = new XElement(xmlns + "Root", new XElement(xmlns + "Firstelement"));

// gives:
<Root xmlns="http://my.namespace">
    <Firstelement />
</Root>