Omettre tous les espaces de noms xsi et xsd lors de la sérialisation d’un object dans .NET?

Le code ressemble à ceci:

SsortingngBuilder builder = new SsortingngBuilder(); XmlWriterSettings settings = new XmlWriterSettings(); settings.OmitXmlDeclaration = true; using (XmlWriter xmlWriter = XmlWriter.Create(builder, settings)) { XmlSerializer s = new XmlSerializer(objectToSerialize.GetType()); s.Serialize(xmlWriter, objectToSerialize); } 

Le document sérialisé résultant comprend des espaces de noms, comme ceci:

  ...  

Pour supprimer les espaces de noms xsi et xsd, je peux suivre la réponse de Comment sérialiser un object en XML sans obtenir xmlns = ”…”? .

Je veux mon tag de message comme (sans atsortingbut d’espace de nommage). Comment puis-je faire ceci?

 ... XmlSerializer s = new XmlSerializer(objectToSerialize.GetType()); XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("",""); s.Serialize(xmlWriter, objectToSerialize, ns); 

Ceci est la deuxième des deux réponses.

Si vous souhaitez simplement supprimer tous les espaces de noms d’un document pendant la sérialisation, vous pouvez le faire en implémentant votre propre XmlWriter.

La méthode la plus simple consiste à dériver de XmlTextWriter et à remplacer la méthode StartElement qui émet des espaces de noms. La méthode StartElement est appelée par le XmlSerializer lors de l’émission d’éléments, y compris la racine. En remplaçant l’espace de noms pour chaque élément et en le remplaçant par la chaîne vide, vous supprimez les espaces de noms de la sortie.

 public class NoNamespaceXmlWriter : XmlTextWriter { //Provide as many contructors as you need public NoNamespaceXmlWriter(System.IO.TextWriter output) : base(output) { Formatting= System.Xml.Formatting.Indented;} public override void WriteStartDocument () { } public override void WriteStartElement(ssortingng prefix, ssortingng localName, ssortingng ns) { base.WriteStartElement("", localName, ""); } } 

Supposons que ce soit le type:

 // explicitly specify a namespace for this type, // to be used during XML serialization. [XmlRoot(Namespace="urn:Abracadabra")] public class MyTypeWithNamespaces { // private fields backing the properties private int _Epoch; private ssortingng _Label; // explicitly define a distinct namespace for this element [XmlElement(Namespace="urn:Whoohoo")] public ssortingng Label { set { _Label= value; } get { return _Label; } } // this property will be implicitly serialized to XML using the // member name for the element name, and inheriting the namespace from // the type. public int Epoch { set { _Epoch= value; } get { return _Epoch; } } } 

Voici comment vous utiliseriez une telle chose lors de la sérialisation:

  var o2= new MyTypeWithNamespaces { ..intializers.. }; var builder = new System.Text.SsortingngBuilder(); using ( XmlWriter writer = new NoNamespaceXmlWriter(new System.IO.SsortingngWriter(builder))) { s2.Serialize(writer, o2, ns2); } Console.WriteLine("{0}",builder.ToSsortingng()); 

Le XmlTextWriter est en quelque sorte cassé, cependant. Selon le document de référence , quand il écrit, il ne vérifie pas ce qui suit:

  • Caractères non valides dans les noms d’atsortingbut et d’élément.

  • Caractères Unicode ne correspondant pas à l’encodage spécifié. Si les caractères Unicode ne correspondent pas au codage spécifié, le XmlTextWriter n’échappe pas aux caractères Unicode dans les entités de caractères.

  • Atsortingbuts en double.

  • Caractères de l’identificateur public ou de l’identificateur système DOCTYPE.

Ces problèmes avec XmlTextWriter existent depuis la version 1.1 du .NET Framework, et ils le restront pour assurer la compatibilité avec les versions antérieures. Si vous ne vous inquiétez pas de ces problèmes, utilisez le XmlTextWriter. Mais la plupart des gens aimeraient un peu plus de fiabilité.

Pour l’obtenir, tout en supprimant les espaces de noms lors de la sérialisation, au lieu de dériver de XmlTextWriter, définissez une implémentation concrète du XmlWriter abstrait et de ses 24 méthodes.

Un exemple est ici:

 public class XmlWriterWrapper : XmlWriter { protected XmlWriter writer; public XmlWriterWrapper(XmlWriter baseWriter) { this.Writer = baseWriter; } public override void Close() { this.writer.Close(); } protected override void Dispose(bool disposing) { ((IDisposable) this.writer).Dispose(); } public override void Flush() { this.writer.Flush(); } public override ssortingng LookupPrefix(ssortingng ns) { return this.writer.LookupPrefix(ns); } public override void WriteBase64(byte[] buffer, int index, int count) { this.writer.WriteBase64(buffer, index, count); } public override void WriteCData(ssortingng text) { this.writer.WriteCData(text); } public override void WriteCharEntity(char ch) { this.writer.WriteCharEntity(ch); } public override void WriteChars(char[] buffer, int index, int count) { this.writer.WriteChars(buffer, index, count); } public override void WriteComment(ssortingng text) { this.writer.WriteComment(text); } public override void WriteDocType(ssortingng name, ssortingng pubid, ssortingng sysid, ssortingng subset) { this.writer.WriteDocType(name, pubid, sysid, subset); } public override void WriteEndAtsortingbute() { this.writer.WriteEndAtsortingbute(); } public override void WriteEndDocument() { this.writer.WriteEndDocument(); } public override void WriteEndElement() { this.writer.WriteEndElement(); } public override void WriteEntityRef(ssortingng name) { this.writer.WriteEntityRef(name); } public override void WriteFullEndElement() { this.writer.WriteFullEndElement(); } public override void WriteProcessingInstruction(ssortingng name, ssortingng text) { this.writer.WriteProcessingInstruction(name, text); } public override void WriteRaw(ssortingng data) { this.writer.WriteRaw(data); } public override void WriteRaw(char[] buffer, int index, int count) { this.writer.WriteRaw(buffer, index, count); } public override void WriteStartAtsortingbute(ssortingng prefix, ssortingng localName, ssortingng ns) { this.writer.WriteStartAtsortingbute(prefix, localName, ns); } public override void WriteStartDocument() { this.writer.WriteStartDocument(); } public override void WriteStartDocument(bool standalone) { this.writer.WriteStartDocument(standalone); } public override void WriteStartElement(ssortingng prefix, ssortingng localName, ssortingng ns) { this.writer.WriteStartElement(prefix, localName, ns); } public override void WriteSsortingng(ssortingng text) { this.writer.WriteSsortingng(text); } public override void WriteSurrogateCharEntity(char lowChar, char highChar) { this.writer.WriteSurrogateCharEntity(lowChar, highChar); } public override void WriteValue(bool value) { this.writer.WriteValue(value); } public override void WriteValue(DateTime value) { this.writer.WriteValue(value); } public override void WriteValue(decimal value) { this.writer.WriteValue(value); } public override void WriteValue(double value) { this.writer.WriteValue(value); } public override void WriteValue(int value) { this.writer.WriteValue(value); } public override void WriteValue(long value) { this.writer.WriteValue(value); } public override void WriteValue(object value) { this.writer.WriteValue(value); } public override void WriteValue(float value) { this.writer.WriteValue(value); } public override void WriteValue(ssortingng value) { this.writer.WriteValue(value); } public override void WriteWhitespace(ssortingng ws) { this.writer.WriteWhitespace(ws); } public override XmlWriterSettings Settings { get { return this.writer.Settings; } } protected XmlWriter Writer { get { return this.writer; } set { this.writer = value; } } public override System.Xml.WriteState WriteState { get { return this.writer.WriteState; } } public override ssortingng XmlLang { get { return this.writer.XmlLang; } } public override System.Xml.XmlSpace XmlSpace { get { return this.writer.XmlSpace; } } } 

Ensuite, fournissez une classe dérivée qui remplace la méthode StartElement, comme précédemment:

 public class NamespaceSupressingXmlWriter : XmlWriterWrapper { //Provide as many contructors as you need public NamespaceSupressingXmlWriter(System.IO.TextWriter output) : base(XmlWriter.Create(output)) { } public NamespaceSupressingXmlWriter(XmlWriter output) : base(XmlWriter.Create(output)) { } public override void WriteStartElement(ssortingng prefix, ssortingng localName, ssortingng ns) { base.WriteStartElement("", localName, ""); } } 

Et puis utilisez cet écrivain comme ça:

  var o2= new MyTypeWithNamespaces { ..intializers.. }; var builder = new System.Text.SsortingngBuilder(); var settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent= true }; using ( XmlWriter innerWriter = XmlWriter.Create(builder, settings)) using ( XmlWriter writer = new NamespaceSupressingXmlWriter(innerWriter)) { s2.Serialize(writer, o2, ns2); } Console.WriteLine("{0}",builder.ToSsortingng()); 

Crédit à Oleg Tkachenko .

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 .

À savoir, 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 nommage de defalt et je pourrais append un préfixe d’espace de noms dans l’ Whoohoo noms Whoohoo dans ma classe MyTypeWithNamespaces :

 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 .

 XmlSerializer sr = new XmlSerializer(objectToSerialize.GetType()); TextWriter xmlWriter = new StreamWriter(filename); XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(); namespaces.Add(ssortingng.Empty, ssortingng.Empty); sr.Serialize(xmlWriter, objectToSerialize, namespaces); 

C’est la première de mes deux réponses à la question.

Si vous souhaitez un contrôle précis des espaces de noms, par exemple si vous souhaitez en supprimer certains, mais pas d’autres, ou si vous souhaitez remplacer un espace de noms par un autre, vous pouvez le faire en utilisant XmlAtsortingbuteOverrides .

Supposons que vous ayez cette définition de type:

 // explicitly specify a namespace for this type, // to be used during XML serialization. [XmlRoot(Namespace="urn:Abracadabra")] public class MyTypeWithNamespaces { // private fields backing the properties private int _Epoch; private ssortingng _Label; // explicitly define a distinct namespace for this element [XmlElement(Namespace="urn:Whoohoo")] public ssortingng Label { set { _Label= value; } get { return _Label; } } // this property will be implicitly serialized to XML using the // member name for the element name, and inheriting the namespace from // the type. public int Epoch { set { _Epoch= value; } get { return _Epoch; } } } 

Et ce pseudo-code de sérialisation:

  var o2= new MyTypeWithNamespaces() { ..initializers...}; ns.Add( "", "urn:Abracadabra" ); XmlSerializer s2 = new XmlSerializer(typeof(MyTypeWithNamespaces)); s2.Serialize(System.Console.Out, o2, ns); 

Vous obtiendrez quelque chose comme ce XML:

   97  

Notez qu’il y a un espace de nommage par défaut sur l’élément racine et qu’il y a aussi un espace de nommage distinct sur l’élément “Label”. Ces espaces de noms ont été dictés par les atsortingbuts décorant le type, dans le code ci-dessus.

La structure de sérialisation XML dans .NET offre la possibilité de remplacer explicitement les atsortingbuts qui décorent le code réel. Vous faites cela avec la classe XmlAtsortingbutesOverrides et les amis. Supposons que je possède le même type et que je le sérialise de la manière suivante:

  // instantiate the container for all atsortingbute overrides XmlAtsortingbuteOverrides xOver = new XmlAtsortingbuteOverrides(); // define a set of XML atsortingbutes to apply to the root element XmlAtsortingbutes xAttrs1 = new XmlAtsortingbutes(); // define an XmlRoot element (as if [XmlRoot] had decorated the type) // The namespace in the atsortingbute override is the empty ssortingng. XmlRootAtsortingbute xRoot = new XmlRootAtsortingbute() { Namespace = ""}; // add that XmlRoot element to the container of atsortingbutes xAttrs1.XmlRoot= xRoot; // add that bunch of atsortingbutes to the container holding all overrides xOver.Add(typeof(MyTypeWithNamespaces), xAttrs1); // create another set of XML Atsortingbutes XmlAtsortingbutes xAttrs2 = new XmlAtsortingbutes(); // define an XmlElement atsortingbute, for a type of "Ssortingng", with no namespace var xElt = new XmlElementAtsortingbute(typeof(Ssortingng)) { Namespace = ""}; // add that XmlElement atsortingbute to the 2nd bunch of atsortingbutes xAttrs2.XmlElements.Add(xElt); // add that bunch of atsortingbutes to the container for the type, and // specifically apply that bunch to the "Label" property on the type. xOver.Add(typeof(MyTypeWithNamespaces), "Label", xAttrs2); // instantiate a serializer with the overrides XmlSerializer s3 = new XmlSerializer(typeof(MyTypeWithNamespaces), xOver); // serialize s3.Serialize(System.Console.Out, o2, ns2); 

Le résultat ressemble à ceci;

   97  

Vous avez enlevé les espaces de noms.

Une question logique est la suivante: pouvez-vous supprimer tous les espaces de noms de types arbitraires lors de la sérialisation, sans passer par les remplacements explicites? La réponse est OUI et comment faire est dans ma prochaine réponse.