¿Cómo evitar atributos xmlns en blanco en la salida de XmlDocument de .NET?

118

Al generar XML a partir de XmlDocument en .NET, xmlnsaparece un atributo en blanco la primera vez que se inserta un elemento sin un espacio de nombres asociado; ¿Cómo se puede prevenir esto?

Ejemplo:

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

Salida:

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

Salida deseada :

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

¿Existe una solución aplicable al XmlDocumentcódigo, no algo que ocurre después de convertir el documento a una cadena con OuterXml?

Mi razonamiento para hacer esto es ver si puedo igualar el XML estándar de un protocolo en particular usando XML generado por XmlDocument. El xmlnsatributo en blanco no puede romper o confundir a un analizador, pero tampoco está presente en ningún uso que haya visto de este protocolo.

Neil C. Obremski
fuente

Respuestas:

111

Gracias a la respuesta de Jeremy Lew y un poco más de juego, descubrí cómo eliminar los xmlnsatributos en blanco : pase el espacio de nombres del nodo raíz al crear cualquier nodo secundario en el que no desee tener un prefijo. El uso de un espacio de nombres sin un prefijo a los medios de raíz que tiene que utilizar el mismo espacio de nombres de elementos secundarios para ellos también no tienen prefijos.

Código fijo:

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

¡Gracias a todos por todas sus respuestas que me llevaron en la dirección correcta!

Neil C. Obremski
fuente
1
Precisamente. Poner el elemento <loner> en el espacio de nombres "cualquiera: espacio de nombres-1.0" significa que el atributo xmlns vacío (que lo coloca en ningún espacio de nombres) no se agregará cuando se serialice. Si necesita una actualización sobre cómo funcionan los espacios de nombres, eche un vistazo a jclark.com/xml/xmlns.htm
JeniT
2
Cuidado: los elementos necesitan esto (o quizás lo mejor doc.DocumentElement.NamespaceURI) pero no especifique un espacio de nombres para el CreateAttribute()que obtendrá xmlns:psomethingincluso si es el mismo uri.
Jason Kleban
87

Esta es una variante de la respuesta de JeniT (¡Muchas gracias por cierto!)

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

Esto elimina tener que copiar o repetir el espacio de nombres en todas partes.

C Johnson
fuente
3
Mejor respuesta según yo. No tenemos que saber qué es el espacio de nombres predeterminado del documento (útil cuando no creamos un archivo xml desde cero, es decir, en escenarios de lectura y modificación).
MuiBienCarlota
11

Si el <loner>elemento en su XML de muestra no tenía la xmlnsdeclaración de espacio de nombres predeterminada, entonces estaría en el whatever:name-space-1.0espacio de nombres en lugar de estar en ningún espacio de nombres. Si eso es lo que desea, debe crear el elemento en ese espacio de nombres:

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

Si desea que el <loner>elemento no esté en un espacio de nombres, entonces el XML que se ha producido es exactamente lo que necesita, y no debe preocuparse por el xmlnsatributo que se agregó automáticamente para usted.

JeniT
fuente
3
El problema radica en los analizadores XML no compatibles (normalmente de Microsoft) que no pueden hacer frente a xmnls = "").
Craig Trader
2
/. llamado. Quieren que les devuelvan su comentario aleatorio sobre la MS.
@W. Craig Trader: no puedo decir que me haya encontrado con eso como un problema. ¿Ejemplo?
Kev
1
Correcto, no quiero que el nodo <loner /> tenga un espacio de nombres, pero tampoco quiero que tenga un atributo de espacio de nombres en blanco (xmlns). Mi razonamiento es solo para ver si puedo hacer coincidir la salida XML de un protocolo en particular que está configurado de esta manera.
Neil C. Obremski
5
No fue un ataque al azar. El bloque de aplicaciones de Microsoft Updater utiliza un manifiesto XML para determinar qué entregar a un cliente. Desafortunadamente, el analizador de manifiestos no puede hacer frente a xmlns = ""; Tuve que escribir un postprocesador que eliminara los atributos xmlns vacíos.
Craig Trader
7

Dado que root está en un espacio de nombres sin prefijo, cualquier hijo de root que quiera tener un espacio de nombres sin nombre tiene que salir como su ejemplo. La solución sería prefijar el elemento raíz así:

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

código:

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);
jlew
fuente
Gracias, pero agregar el espacio de nombres a la raíz real rompería mi XML en relación con el protocolo particular con el que estoy trabajando.
Neil C. Obremski
¡Ah! Me di cuenta más de lo que estabas diciendo y lo tomé en cuenta al escribir mi propia respuesta. Gracias Jeremy
Neil C. Obremski
0

Si es posible, cree una clase de serialización y luego haga lo siguiente:

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

Es más seguro y puede controlar los espacios de nombres con atributos si realmente necesita más control.

ilitirit
fuente
0

Resolví el problema usando Factory Pattern. Creé una fábrica para objetos XElement. Como parámetro para la creación de instancias de la fábrica, he especificado un objeto XNamespace. Por lo tanto, cada vez que la fábrica crea un XElement, el espacio de nombres se agregará automáticamente. Aquí está el código de fábrica:

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);
    }
}
brinke
fuente
1
El OP estaba preguntando XmlDocument, no XDocument.
John Saunders
0

Sí, puede evitar XMLNS del XmlElement. El tiempo de la primera creación se acerca: así

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

Cambie el código: y pase el espacio de nombres xml como este

Código C #:

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