126 votes

En omettant tous les espaces de noms xsi et xsd lors de la sérialisation d’un objet en .NET ?

Le code ressemble à ceci :

Le document sérialisé résultant inclut des espaces de noms, comme suit :

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

Je veux ma balise de message sous la forme `` (sans les attributs d’espace de noms). Comment est-ce que je peux faire cela ?

229voto

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

27voto

Cheeso Points 87022

C'est le 2ème des deux réponses.

Si vous souhaitez simplement enlever tous les espaces de noms arbitrairement à partir d'un document au cours de la sérialisation, vous pouvez le faire par la mise en œuvre de votre propre XmlWriter.

Le plus simple est de dériver de XmlTextWriter et de redéfinir la méthode StartElement qui émet des espaces de noms. La méthode StartElement est invoquée par le XmlSerializer en émettant tous les éléments, y compris la racine. En substituant l'espace de noms pour chaque élément, et de le remplacer avec la chaîne vide, vous avez dépouillé les espaces de noms à partir 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(string prefix, string localName, string ns)
    {
        base.WriteStartElement("", localName, "");
    }
}

Supposons que c'est 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 string _Label;

    // explicitly define a distinct namespace for this element
    [XmlElement(Namespace="urn:Whoohoo")]
    public string 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 pouvez l'utiliser telle chose lors de la sérialisation:

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

Le XmlTextWriter est une sorte de cassé, si. Selon le document de référence, lorsqu'il écrit qu'il n'a pas vérifiez les points suivants:

  • Caractères non valides dans les attributs et les noms d'éléments.

  • Les caractères Unicode qui ne correspondent pas à l'encodage spécifié. Si l'Unicode les personnages ne correspondent pas spécifié codage, le XmlTextWriter n' l'échappement des caractères Unicode les entités de caractères.

  • Double attributs.

  • Les caractères dans la DÉCLARATION du public identifiant ou le système d'identifiant.

Ces problèmes avec XmlTextWriter ont été autour depuis v1.1 de la .NET Framework, et ils resteront, pour des raisons de compatibilité descendante. Si vous n'avez pas de préoccupations au sujet de ces problèmes, puis par tous les moyens d'utiliser les XmlTextWriter. Mais la plupart des gens aimeraient un peu plus de fiabilité.

Pour l'obtenir, tout en supprimant encore les espaces de noms au cours de la sérialisation, au lieu de dériver de XmlTextWriter, de définir une mise en œuvre concrète de l'abstrait XmlWriter et 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 string LookupPrefix(string 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(string 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(string text)
    {
        this.writer.WriteComment(text);
    }

    public override void WriteDocType(string name, string pubid, string sysid, string subset)
    {
        this.writer.WriteDocType(name, pubid, sysid, subset);
    }

    public override void WriteEndAttribute()
    {
        this.writer.WriteEndAttribute();
    }

    public override void WriteEndDocument()
    {
        this.writer.WriteEndDocument();
    }

    public override void WriteEndElement()
    {
        this.writer.WriteEndElement();
    }

    public override void WriteEntityRef(string name)
    {
        this.writer.WriteEntityRef(name);
    }

    public override void WriteFullEndElement()
    {
        this.writer.WriteFullEndElement();
    }

    public override void WriteProcessingInstruction(string name, string text)
    {
        this.writer.WriteProcessingInstruction(name, text);
    }

    public override void WriteRaw(string data)
    {
        this.writer.WriteRaw(data);
    }

    public override void WriteRaw(char[] buffer, int index, int count)
    {
        this.writer.WriteRaw(buffer, index, count);
    }

    public override void WriteStartAttribute(string prefix, string localName, string ns)
    {
        this.writer.WriteStartAttribute(prefix, localName, ns);
    }

    public override void WriteStartDocument()
    {
        this.writer.WriteStartDocument();
    }

    public override void WriteStartDocument(bool standalone)
    {
        this.writer.WriteStartDocument(standalone);
    }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        this.writer.WriteStartElement(prefix, localName, ns);
    }

    public override void WriteString(string text)
    {
        this.writer.WriteString(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(string value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteWhitespace(string 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 string XmlLang
    {
        get
        {
            return this.writer.XmlLang;
        }
    }

    public override System.Xml.XmlSpace XmlSpace
    {
        get
        {
            return this.writer.XmlSpace;
        }
    }        
}

Ensuite, fournir une classe dérivée qui remplace la méthode StartElement, comme avant:

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(string prefix, string localName, string ns)
    {
        base.WriteStartElement("", localName, "");
    }
}

Et ensuite utiliser cet écrivain de la sorte:

        var o2= new MyTypeWithNamespaces { ..intializers.. };
        var builder = new System.Text.StringBuilder();
        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.ToString());

Crédit pour ce Oleg Tkachenko.

15voto

fourpastmidnight Points 1577

Après la lecture de la documentation de Microsoft et plusieurs solutions en ligne, j'ai découvert la solution à ce problème. Il fonctionne à la fois avec le haut- XmlSerializer personnalisé et de la sérialisation XML via IXmlSerialiazble.

À la pentecôte, je vais utiliser le même MyTypeWithNamespaces XML de l'échantillon qui a été utilisé dans les réponses à cette question jusqu'à présent.

[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 
    // XmlNamespacesDeclarationAttribute, 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(string.Empty, string.Empty),  // And don't do this:
            // new XmlQualifiedName("", "")

            // DO THIS:
            new XmlQualifiedName(string.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(string 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 string Label
    {
        get { return this._label; }
        set { this._label = value; }
    }
    private string _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
    // attribute.
    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces Namespaces
    {
        get { return this._namespaces; }
    }
    private XmlSerializerNamespaces _namespaces;
}

C'est tout pour cette classe. Maintenant, certains ont contesté avoir un XmlSerializerNamespaces objet quelque part à l'intérieur de leurs classes; mais comme vous pouvez le voir, j'ai discrètement loin dans le constructeur par défaut et un exposé public des biens de retour les espaces de noms.

Maintenant, quand vient le temps de sérialiser la classe, vous pouvez utiliser 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 XmlAttributeOverrides(),
    new Type[]{}, new XmlRootAttribute("MyTypeWithNamespaces"), "urn:Abracadabra");

******/
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
    new XmlRootAttribute("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:

<MyTypeWithNamespaces>
    <Label xmlns="urn:Whoohoo">myLabel</Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

J'ai utilisé avec succès cette méthode dans un récent projet avec une profondeur de la hiérarchie de classes qui sont sérialisé en XML pour les appels de service web. La documentation de Microsoft n'est pas très clair à propos de quoi faire avec le public accesible XmlSerializerNamespaces membre une fois que vous avez créé, et donc beaucoup de gens pensent que c'est inutile. Mais par suite de leur documentation et de l'utiliser de la manière illustrée ci-dessus, vous pouvez personnaliser la façon dont le XmlSerializer génère des fichiers XML pour vos classes sans recourir à la non prise en charge du comportement ou de "rouler votre propre" sérialisation par la mise en œuvre de IXmlSerializable.

Il est mon espoir que cette réponse sera mis au repos, une fois pour toutes, comment se débarrasser de la norme xsi et xsd espaces de noms générés par l' XmlSerializer.

Mise à JOUR: je veux juste m'assurer que j'ai répondu à l'OP de la question sur la suppression de tous les espaces de noms. Mon code ci-dessus va travailler pour cela, laissez-moi vous montrer comment. Maintenant, dans l'exemple ci-dessus, vous ne pouvez vraiment pas se débarrasser de tous les espaces de noms (parce qu'il y a deux espaces de noms en cours d'utilisation). Quelque part dans votre document XML, vous allez avoir besoin d'avoir quelque chose comme xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo. Si la classe de l'exemple est une partie d'un document plus grand, puis, quelque part au-dessus d'un espace de noms doit être déclarée, soit l'un des (ou les deux) Abracadbra et Whoohoo. Si non, alors l'élément dans l'une ou des deux espaces de noms doivent être décorées avec un préfixe de quelque sorte (vous ne pouvez pas avoir deux espaces de noms par défaut, non?). Donc, pour cet exemple, Abracadabra est le defalt espace de noms. J'ai pu à l'intérieur de mon MyTypeWithNamespaces de la classe d'ajouter un préfixe d'espace de noms pour l' Whoohoo de l'espace de noms comme:

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

Maintenant, dans ma définition de la classe, j'ai indiqué que l' <Label/> élément est dans l'espace de noms "urn:Whoohoo", je n'ai pas besoin d'en faire plus. Lorsque j'ai maintenant sérialiser la classe à l'aide de mon ci-dessus code de sérialisation inchangé, c'est la sortie:

<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
    <w:Label>myLabel</w:Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

Parce qu' <Label> est dans un espace de noms différent du reste du document, il doit, d'une certaine manière, être "décoré" avec un espace de noms. Remarquez qu'il y a toujours pas d' xsi et xsd espaces de noms.

6voto

Cheeso Points 87022

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

Si vous souhaitez un contrôle plus fin des les espaces de noms - par exemple, si vous souhaitez omettre certains d'entre eux, mais pas d'autres, ou si vous souhaitez remplacer un espace de noms avec un autre, vous pouvez le faire à l'aide de XmlAttributeOverrides.

Supposons que vous avez ce type de définition:

// 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 string _Label;

    // explicitly define a distinct namespace for this element
    [XmlElement(Namespace="urn:Whoohoo")]
    public string 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 cette sérialisation pseudo-code:

        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 ceci XML:

<MyTypeWithNamespaces xmlns="urn:Abracadabra">
  <Label xmlns="urn:Whoohoo">Cimsswybclaeqjh</Label>
  <Epoch>97</Epoch>
</MyTypeWithNamespaces>

Notez qu'il existe un espace de noms par défaut sur l'élément racine, et il y a aussi un espace de noms distinct sur le "Label" de l'élément. Ces espaces de noms ont été dictées par les attributs de la décoration du type, dans le code ci-dessus.

La Sérialisation Xml cadre .NET inclut la possibilité explicitement remplacer les attributs qui décorent le code réel. Vous faites cela avec la XmlAttributesOverrides de classe et amis. Supposons que j'ai le même type, et je sérialiser de cette façon:

        // instantiate the container for all attribute overrides
        XmlAttributeOverrides xOver = new XmlAttributeOverrides();

        // define a set of XML attributes to apply to the root element
        XmlAttributes xAttrs1 = new XmlAttributes();

        // define an XmlRoot element (as if [XmlRoot] had decorated the type)
        // The namespace in the attribute override is the empty string. 
        XmlRootAttribute xRoot = new XmlRootAttribute() { Namespace = ""};

        // add that XmlRoot element to the container of attributes
        xAttrs1.XmlRoot= xRoot;

        // add that bunch of attributes to the container holding all overrides
        xOver.Add(typeof(MyTypeWithNamespaces), xAttrs1);

        // create another set of XML Attributes
        XmlAttributes xAttrs2 = new XmlAttributes();

        // define an XmlElement attribute, for a type of "String", with no namespace
        var xElt = new XmlElementAttribute(typeof(String)) { Namespace = ""};

        // add that XmlElement attribute to the 2nd bunch of attributes
        xAttrs2.XmlElements.Add(xElt);

        // add that bunch of attributes 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;

<MyTypeWithNamespaces>
  <Label>Cimsswybclaeqjh</Label>
  <Epoch>97</Epoch>
</MyTypeWithNamespaces>

Vous avez dépouillé les espaces de noms.

Une logique de la question est, pouvez-vous bande de tous les espaces de noms de types arbitraires lors de la sérialisation, sans passer par l'explicite remplace? La réponse est OUI, et comment faire, il est dans ma prochaine réponse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X