Comment empêcher les attributs xmlns vides dans la sortie de XmlDocument .NET?

118

Lors de la génération de XML à partir de XmlDocument dans .NET, un xmlnsattribut vide apparaît la première fois qu'un élément sans espace de noms associé est inséré; comment peut-on éviter cela?

Exemple:

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

Production:

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

Sortie souhaitée :

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

Existe-t-il une solution applicable au XmlDocumentcode, pas quelque chose qui se produit après la conversion du document en chaîne avec OuterXml?

Mon raisonnement pour faire cela est de voir si je peux faire correspondre le XML standard d'un protocole particulier en utilisant le XML généré par XmlDocument. L' xmlnsattribut vide peut ne pas casser ou confondre un analyseur, mais il n'est pas non plus présent dans les utilisations que j'ai vues de ce protocole.

Neil C. Obremski
la source

Réponses:

111

Grâce à la réponse de Jeremy Lew et à un peu plus de jeu, j'ai compris comment supprimer les xmlnsattributs vides : passez dans l'espace de noms du nœud racine lors de la création de tout nœud enfant sur lequel vous ne voulez pas avoir de préfixe. L' utilisation d' un espace de noms sans préfixe au moyen de racine que vous devez utiliser ce même espace de noms sur des éléments de l' enfant pour eux à également pas préfixes.

Code fixe:

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

Merci à tous pour toutes vos réponses qui m'ont conduit dans la bonne direction!

Neil C. Obremski
la source
1
Précisément. Mettre l'élément <loner> dans l'espace de noms "what: name-space-1.0" signifie que l'attribut xmlns vide (qui ne le place dans aucun espace de noms) ne lui sera pas ajouté lorsqu'il sera sérialisé. Si vous avez besoin d'un rafraîchissement sur le fonctionnement des espaces de noms, jetez un œil à jclark.com/xml/xmlns.htm
JeniT
2
Attention: les éléments en ont besoin (ou peut-être mieux doc.DocumentElement.NamespaceURI) mais ne spécifiez pas d'espace de nom pour CreateAttribute()vous obtiendrez le xmlns:psomethingmême s'il s'agit du même uri.
Jason Kleban
87

Ceci est une variante de la réponse de JeniT (Merci beaucoup btw!)

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

Cela évite d'avoir à copier ou répéter l'espace de noms partout.

C Johnson
la source
3
Meilleure réponse selon moi. Nous n'avons pas besoin de savoir ce qu'est l'espace de noms par défaut du document (utile lorsque nous ne créons pas un fichier xml à partir de zéro, c'est-à-dire dans des scénarios de lecture et de modification).
MuiBienCarlota
11

Si l' <loner>élément de votre exemple de XML ne contenait pas la xmlnsdéclaration d'espace de noms par défaut, il se trouverait dans l' whatever:name-space-1.0espace de noms plutôt que dans aucun espace de noms. Si c'est ce que vous voulez, vous devez créer l'élément dans cet espace de noms:

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

Si vous voulez que l' <loner>élément se trouve dans aucun espace de noms, alors le XML qui a été produit est exactement ce dont vous avez besoin, et vous ne devriez pas vous soucier de l' xmlnsattribut qui a été ajouté automatiquement pour vous.

JeniT
la source
3
Le problème réside dans les analyseurs XML non conformes (généralement de Microsoft) qui ne peuvent pas gérer xmnls = "").
Craig Trader
2
/. appelé. Ils veulent leur retour de commentaires aléatoires sur MS
@W. Craig Trader - je ne peux pas dire que j'ai rencontré cela comme un problème. Exemple?
Kev
1
Correct, je ne veux pas que le nœud <loner /> ait un espace de noms, mais je ne veux pas non plus qu'il ait un attribut d'espace de noms vide (xmlns). Mon raisonnement est juste de voir si je peux faire correspondre la sortie XML d'un protocole particulier qui est configuré comme ça.
Neil C.Obremski
5
Ce n'était pas un dénigrement aléatoire. Le bloc d'application Microsoft Updater utilise un manifeste XML pour déterminer ce qu'il faut remettre à un client. Malheureusement, l'analyseur du manifeste ne peut pas gérer xmlns = ""; J'ai dû écrire un post-processeur qui supprimerait les attributs xmlns vides.
Craig Trader
7

Puisque root est dans un espace de noms sans préfixe, tout enfant de root qui veut être sans espace de noms doit être affiché comme dans votre exemple. La solution serait de préfixer l'élément racine comme ceci:

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

code:

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
la source
Merci, mais l'ajout de l'espace de noms à la racine réelle briserait mon XML par rapport au protocole particulier avec lequel je travaille.
Neil C.Obremski
Ah! J'ai davantage compris ce que vous disiez et en ai tenu compte lors de la rédaction de ma propre réponse. Merci Jeremy
Neil C. Obremski
0

Si possible, créez une classe de sérialisation puis faites:

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

C'est plus sûr et vous pouvez contrôler les espaces de noms avec des attributs si vous avez vraiment besoin de plus de contrôle.

ilitirit
la source
0

J'ai résolu le problème en utilisant le modèle d'usine. J'ai créé une fabrique pour les objets XElement. En tant que paramètre pour l'instanciation de l'usine, j'ai spécifié un objet XNamespace. Ainsi, à chaque fois qu'un XElement est créé par la fabrique, l'espace de noms sera ajouté automatiquement. Voici le code de l'usine:

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
la source
1
Le PO demandait XmlDocument, non XDocument.
John Saunders
0

Oui, vous pouvez empêcher le XMLNS de XmlElement. Le temps de la première création arrive: comme ça

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

Changez le code: et passez l'espace de noms XML comme ceci

Code 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
la source