XmlSerializer: supprime les espaces de noms xsi et xsd inutiles

Existe-t-il un moyen de configurer le XmlSerializer pour qu’il n’écrive pas les espaces de noms par défaut dans l’élément racine?

Ce que je reçois c’est ceci:

   

et je veux supprimer les deux déclarations xmlns.

Dupliquer de : Comment sérialiser un object en XML sans obtenir xmlns = ”…”?

Puisque Dave m’a demandé de répéter ma réponse à Omettre tous les espaces de noms xsi et xsd lors de la sérialisation d’un object dans .NET , j’ai mis à jour ce message et répété ma réponse ici à partir du lien susmentionné. L’exemple utilisé dans cette réponse est le même exemple que celui utilisé pour l’autre question. Ce qui suit est copié, textuellement.


Après avoir lu la documentation de Microsoft et plusieurs solutions en ligne, j’ai découvert la solution à ce problème. Il fonctionne à la fois avec le XmlSerializer intégré et la sérialisation XML personnalisée via IXmlSerialiazble .

Pour ce faire, j’utiliserai le même MyTypeWithNamespaces XML MyTypeWithNamespaces qui a été utilisé jusqu’à présent dans les réponses à cette question.

 [XmlRoot("MyTypeWithNamespaces", Namespace="urn:Abracadabra", IsNullable=false)] public class MyTypeWithNamespaces { // As noted below, per Microsoft's documentation, if the class exposes a public // member of type XmlSerializerNamespaces decorated with the // XmlNamespacesDeclarationAtsortingbute, then the XmlSerializer will utilize those // namespaces during serialization. public MyTypeWithNamespaces( ) { this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] { // Don't do this!! Microsoft's documentation explicitly says it's not supported. // It doesn't throw any exceptions, but in my testing, it didn't always work. // new XmlQualifiedName(ssortingng.Empty, ssortingng.Empty), // And don't do this: // new XmlQualifiedName("", "") // DO THIS: new XmlQualifiedName(ssortingng.Empty, "urn:Abracadabra") // Default Namespace // Add any other namespaces, with prefixes, here. }); } // If you have other constructors, make sure to call the default constructor. public MyTypeWithNamespaces(ssortingng label, int epoch) : this( ) { this._label = label; this._epoch = epoch; } // An element with a declared namespace different than the namespace // of the enclosing type. [XmlElement(Namespace="urn:Whoohoo")] public ssortingng Label { get { return this._label; } set { this._label = value; } } private ssortingng _label; // An element whose tag will be the same name as the property name. // Also, this element will inherit the namespace of the enclosing type. public int Epoch { get { return this._epoch; } set { this._epoch = value; } } private int _epoch; // Per Microsoft's documentation, you can add some public member that // returns a XmlSerializerNamespaces object. They use a public field, // but that's sloppy. So I'll use a private backed-field with a public // getter property. Also, per the documentation, for this to work with // the XmlSerializer, decorate it with the XmlNamespaceDeclarations // atsortingbute. [XmlNamespaceDeclarations] public XmlSerializerNamespaces Namespaces { get { return this._namespaces; } } private XmlSerializerNamespaces _namespaces; } 

C’est tout pour cette classe. Maintenant, certains ont objecté à avoir un object XmlSerializerNamespaces quelque part dans leurs classes; mais comme vous pouvez le voir, je l’ai soigneusement rangé dans le constructeur par défaut et exposé une propriété publique pour renvoyer les espaces de noms.

Maintenant, quand vient le temps de sérialiser la classe, vous utiliserez le code suivant:

 MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42); /****** OK, I just figured I could do this to make the code shorter, so I commented out the below and replaced it with what follows: // You have to use this constructor in order for the root element to have the right namespaces. // If you need to do custom serialization of inner objects, you can use a shortened constructor. XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces), new XmlAtsortingbuteOverrides(), new Type[]{}, new XmlRootAtsortingbute("MyTypeWithNamespaces"), "urn:Abracadabra"); ******/ XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces), new XmlRootAtsortingbute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" }); // I'll use a MemoryStream as my backing store. MemoryStream ms = new MemoryStream(); // This is extra! If you want to change the settings for the XmlSerializer, you have to create // a separate XmlWriterSettings object and use the XmlTextWriter.Create(...) factory method. // So, in this case, I want to omit the XML declaration. XmlWriterSettings xws = new XmlWriterSettings(); xws.OmitXmlDeclaration = true; xws.Encoding = Encoding.UTF8; // This is probably the default // You could use the XmlWriterSetting to set indenting and new line options, but the // XmlTextWriter class has a much easier method to accomplish that. // The factory method returns a XmlWriter, not a XmlTextWriter, so cast it. XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws); // Then we can set our indenting options (this is, of course, optional). xtw.Formatting = Formatting.Indented; // Now serialize our object. xs.Serialize(xtw, myType, myType.Namespaces); 

Une fois que vous avez fait cela, vous devriez obtenir le résultat suivant:

   42  

J’ai utilisé cette méthode avec succès dans un projet récent avec une hiérarchie profonde de classes sérialisées en XML pour les appels de service Web. La documentation de Microsoft n’est pas très claire sur ce qu’il faut faire avec le membre XmlSerializerNamespaces accessible au public une fois que vous l’avez créé, et beaucoup pensent que c’est inutile. Mais en suivant leur documentation et en l’utilisant de la manière indiquée ci-dessus, vous pouvez personnaliser la manière dont XmlSerializer génère le code XML pour vos classes sans recourir à un comportement non pris en charge ou à la “sérialisation” en implémentant IXmlSerializable .

J’espère que cette réponse permettra, une fois pour toutes, de se débarrasser des espaces de noms xsi et xsd standard générés par XmlSerializer .

MISE À JOUR: Je veux juste m’assurer que j’ai répondu à la question de l’OP concernant la suppression de tous les espaces de noms. Mon code ci-dessus fonctionnera pour cela; Laisse moi te montrer comment. Maintenant, dans l’exemple ci-dessus, vous ne pouvez vraiment pas vous débarrasser de tous les espaces de noms (car il y a deux espaces de noms utilisés). Quelque part dans votre document XML, vous devrez avoir quelque chose comme xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo . Si la classe dans l’exemple fait partie d’un document plus grand, alors quelque part au-dessus d’un espace de noms doit être déclaré pour l’un ou l’autre (ou les deux) Abracadbra et Whoohoo . Sinon, l’élément dans l’un ou les deux espaces de noms doit être décoré avec un préfixe quelconque (vous ne pouvez pas avoir deux espaces de noms par défaut, non?) Donc, pour cet exemple, Abracadabra est l’espace de noms par défaut, mais dans ma classe MyTypeWithNamespaces je MyTypeWithNamespaces append un préfixe d’espace de noms pour l’ Whoohoo noms Whoohoo comme ceci:

 public MyTypeWithNamespaces { this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] { new XmlQualifiedName(ssortingng.Empty, "urn:Abracadabra"), // Default Namespace new XmlQualifiedName("w", "urn:Whoohoo") }); } 

Maintenant, dans ma définition de classe, j’ai indiqué que l’élément est dans l’espace de noms "urn:Whoohoo" , donc je n’ai rien d’autre à faire. Quand je sérialise maintenant la classe en utilisant mon code de sérialisation ci-dessus inchangé, c’est la sortie:

  myLabel 42  

Comme trouve dans un espace de noms différent du rest du document, il doit, d’une certaine manière, être “décoré” avec un espace de noms. Notez qu’il n’y a toujours pas d’espaces de noms xsi et xsd .


Cela met fin à ma réponse à l’autre question. Mais je voulais m’assurer que je répondais à la question de l’OP concernant l’utilisation d’aucun espace de noms, car j’estime que je ne l’ai pas encore vraiment abordé. Supposons que fasse partie du même espace de noms que le rest du document, dans ce cas urn:Abracadabra :

   

Votre constructeur ressemblerait à ce que serait le premier exemple de code, avec la propriété publique pour récupérer l’espace de noms par défaut:

 // As noted below, per Microsoft's documentation, if the class exposes a public // member of type XmlSerializerNamespaces decorated with the // XmlNamespacesDeclarationAtsortingbute, then the XmlSerializer will utilize those // namespaces during serialization. public MyTypeWithNamespaces( ) { this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] { new XmlQualifiedName(ssortingng.Empty, "urn:Abracadabra") // Default Namespace }); } [XmlNamespaceDeclarations] public XmlSerializerNamespaces Namespaces { get { return this._namespaces; } } private XmlSerializerNamespaces _namespaces; 

Ensuite, dans votre code qui utilise l’object MyTypeWithNamespaces pour le sérialiser, vous l’appellerez comme ci-dessus:

 MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42); XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces), new XmlRootAtsortingbute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" }); ... // Above, you'd setup your XmlTextWriter. // Now serialize our object. xs.Serialize(xtw, myType, myType.Namespaces); 

Et le XmlSerializer cracherait le même XML que celui indiqué ci-dessus, sans espaces de noms supplémentaires dans la sortie:

   
 //Create our own namespaces for the output XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); //Add an empty namespace and empty value ns.Add("", ""); //Create the serializer XmlSerializer slz = new XmlSerializer(someType); //Serialize the object with our own namespaces (notice the overload) slz.Serialize(myXmlTextWriter, someObject, ns) 

Il existe une alternative – vous pouvez fournir un membre de type XmlSerializerNamespaces dans le type à sérialiser. Décorez-le avec l’atsortingbut XmlNamespaceDeclarations . Ajoutez les préfixes d’espace de noms et les URI à ce membre. Ensuite, toute sérialisation qui ne fournit pas explicitement un XmlSerializerNamespaces utilisera le préfixe d’espace de noms + les paires d’URI que vous avez insérées dans votre type.

Exemple de code, supposons que ce soit votre type:

 [XmlRoot(Namespace = "urn:mycompany.2009")] public class Person { [XmlAtsortingbute] public bool Known; [XmlElement] public ssortingng Name; [XmlNamespaceDeclarations] public XmlSerializerNamespaces xmlns; } 

Tu peux le faire:

 var p = new Person { Name = "Charley", Known = false, xmlns = new XmlSerializerNamespaces() } p.xmlns.Add("",""); // default namespace is emoty p.xmlns.Add("c", "urn:mycompany.2009"); 

Et cela signifie que toute sérialisation de cette instance qui ne spécifie pas son propre ensemble de préfixes + paires d’URI utilisera le préfixe “p” pour l’espace de noms “urn: mycompany.2009”. Il omettra également les espaces de noms xsi et xsd.

La différence ici est que vous ajoutez le XmlSerializerNamespaces au type lui-même, plutôt que de l’utiliser explicitement lors d’un appel à XmlSerializer.Serialize (). Cela signifie que si une instance de votre type est sérialisée par du code que vous ne possédez pas (par exemple dans une stack de services Web) et que ce code ne fournit pas explicitement un XmlSerializerNamespaces, ce sérialiseur utilisera les espaces de noms fournis dans l’instance.