Как предотвратить пустые атрибуты xmlns в выводе из XmlDocument .NET?

При генерации XML из XmlDocument в .NET пустой атрибут xmlns появляется при первой вставке элемента без связанного пространства имен; как это можно предотвратить?

Пример:

XmlDocument xml = new XmlDocument();
xml.AppendChild(xml.CreateElement("root",
    "whatever:name-space-1.0"));
xml.DocumentElement.AppendChild(xml.CreateElement("loner"));
Console.WriteLine(xml.OuterXml);

Вывод:

<root xmlns="whatever:name-space-1.0"><loner xmlns="" /></root>

Желаемый результат:

<root xmlns="whatever:name-space-1.0"><loner /></root>

Есть ли решение, применимое к коду XmlDocument, а не к тому, что происходит после преобразования документа в строку с OuterXml?

Я делаю это, чтобы посмотреть, смогу ли я сопоставить стандартный XML определенного протокола с помощью XML, созданного XmlDocument. Пустой атрибут xmlns может не сломать или запутать синтаксический анализатор, но он также не присутствует ни в каком использовании этого протокола, которое я видел.


person Neil C. Obremski    schedule 25.09.2008    source источник


Ответы (7)


Благодаря ответу Джереми Лью и небольшому количеству экспериментов я понял, как удалить пустые атрибуты xmlns: передать пространство имен корневого узла при создании любого дочернего узла, на котором не должен быть префикс. Использование пространства имен без префикса в корне означает, что вам нужно использовать то же пространство имен для дочерних элементов, чтобы они также не имели префиксов.

Фиксированный код:

XmlDocument xml = new XmlDocument();
xml.AppendChild(xml.CreateElement("root", "whatever:name-space-1.0"));
xml.DocumentElement.AppendChild(xml.CreateElement("loner", "whatever:name-space-1.0")); 
Console.WriteLine(xml.OuterXml);

Спасибо всем за все ваши ответы, которые привели меня в правильном направлении!

person Neil C. Obremski    schedule 25.09.2008
comment
Точно. Помещение элемента ‹loner› в пространство имен Any: name-space-1.0 означает, что пустой атрибут xmlns (который не помещает его в пространство имен) не будет добавлен к нему при сериализации. Если вам нужно обновить, как работают пространства имен, взгляните на jclark.com/xml/xmlns.htm - person JeniT; 26.09.2008
comment
Осторожно: элементы нуждаются в этом (или, возможно, лучше doc.DocumentElement.NamespaceURI), но не указывайте пространство имен для CreateAttribute(), иначе вы получите xmlns:psomething, даже если это тот же самый uri. - person Jason Kleban; 15.07.2015

Это вариант ответа JeniT (кстати, большое спасибо!)

XmlElement new_element = doc.CreateElement("Foo", doc.DocumentElement.NamespaceURI);

Это избавляет от необходимости копировать или повторять пространство имен везде.

person C Johnson    schedule 30.12.2011
comment
На мой взгляд, лучший ответ. Нам не нужно знать, что такое пространство имен документа по умолчанию (полезно, когда мы не создаем XML-файл с нуля, например, в сценариях чтения и изменения). - person MuiBienCarlota; 04.10.2012

Если элемент <loner> в вашем примере XML не содержит объявления пространства имен xmlns по умолчанию, тогда он будет в пространстве имен whatever:name-space-1.0, а не в пространстве имен. Если это то, что вы хотите, вам нужно создать элемент в этом пространстве имен:

xml.CreateElement("loner", "whatever:name-space-1.0")

Если вы хотите, чтобы элемент <loner> не находился в пространстве имен, то созданный XML - это именно то, что вам нужно, и вам не следует беспокоиться об атрибуте xmlns, который был добавлен автоматически для вас.

person JeniT    schedule 25.09.2008
comment
Проблема заключается в несовместимых парсерах XML (обычно от Microsoft), которые не могут справиться с xmnls =). - person Craig Trader; 25.09.2008
comment
/. называется. Они хотят получить назад свой случайный комментарий с трепом о MS. - person ; 25.09.2008
comment
@W. Крейг Трейдер: не могу сказать, что столкнулся с этим как с проблемой. Пример? - person Kev; 25.09.2008
comment
Правильно, я не хочу, чтобы у узла ‹loner /› было пространство имен, но я также не хочу, чтобы у него был пустой атрибут пространства имен (xmlns). Мое рассуждение состоит в том, чтобы просто посмотреть, смогу ли я сопоставить вывод XML определенного протокола, который настроен таким образом. - person Neil C. Obremski; 25.09.2008
comment
Это не была случайная трепка. Блок приложения Microsoft Updater использует XML-манифест, чтобы определить, что доставить клиенту. К сожалению, парсер манифеста не справляется с xmlns =; Мне пришлось написать постпроцессор, который удалял бы пустые атрибуты xmlns. - person Craig Trader; 26.09.2008

Поскольку root находится в пространстве имен без префикса, любой дочерний элемент root, который хочет быть без пространства имен, должен выводиться, как ваш пример. Решением было бы префикс корневого элемента следующим образом:

<w:root xmlns:w="whatever:name-space-1.0">
   <loner/>
</w:root>

код:

XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement( "w", "root", "whatever:name-space-1.0" );
doc.AppendChild( root );
root.AppendChild( doc.CreateElement( "loner" ) );
Console.WriteLine(doc.OuterXml);
person jlew    schedule 25.09.2008
comment
Спасибо, но добавление пространства имен к фактическому корню нарушит мой XML по отношению к конкретному протоколу, с которым я работаю. - person Neil C. Obremski; 25.09.2008
comment
Ах! Я лучше понял, о чем вы говорите, и учел это при написании собственного ответа. Спасибо Джереми - person Neil C. Obremski; 26.09.2008

Если возможно, создайте класс сериализации, а затем выполните:

XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer serializer = new XmlSerializer(yourType);
serializer.Serialize(xmlTextWriter, someObject, ns);

Это безопаснее, и вы можете управлять пространствами имен с помощью атрибутов, если вам действительно нужно больше контроля.

person ilitirit    schedule 25.09.2008

Я решил проблему, используя Factory Pattern. Я создал фабрику для объектов XElement. В качестве параметра для создания фабрики я указал объект XNamespace. Итак, каждый раз, когда фабрика создает XElement, пространство имен будет добавляться автоматически. Вот код фабрики:

internal class XElementFactory
{
    private readonly XNamespace currentNs;

    public XElementFactory(XNamespace ns)
    {
        this.currentNs = ns;
    }

    internal XElement CreateXElement(String name, params object[] content)
    {
        return new XElement(currentNs + name, content);
    }
}
person brinke    schedule 23.02.2011
comment
OP спрашивал о XmlDocument, а не о XDocument. - person John Saunders; 06.06.2015

Да, вы можете запретить XMLNS из XmlElement. Время первого творения приближается: вот так

<trkpt lat="30.53597" lon="-97.753324" xmlns="">
    <ele>249.118774</ele>
    <time>2006-05-05T14:34:44Z</time>
</trkpt>

Измените код: и передайте пространство имен xml следующим образом

Код C #:

XmlElement bookElement = xdoc.CreateElement("trkpt", "http://www.topografix.com/GPX/1/1");
bookElement.SetAttribute("lat", "30.53597");
bookElement.SetAttribute("lon", "97.753324");
person Debabrata Ghosh    schedule 06.06.2015