157 votes

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:

 <?xml ...>
<rootelement xmlns:xsi="..." xmlns:xsd="...">
</rootelement>
 

et je veux supprimer les deux déclarations xmlns.

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

293voto

Jeremy Points 4188
//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)

73voto

fourpastmidnight Points 1577

Depuis que Dave a demandé pour moi de me répéter ma réponse à Omettre tout xsi et xsd espaces de noms lors de la sérialisation d'un objet .NET, j'ai mis à jour ce post et répété ma réponse ici, à partir de l'adresse ci-dessus le lien. L'exemple utilisé dans cette réponse, c'est le même exemple utilisé pour l'autre question. Ce qui suit est copié mot à mot.


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 l'espace de noms par défaut. 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.


C'est la fin de ma réponse à l'autre question. Mais je voulais m'assurer que j'ai répondu à l'OP de la question sur l'utilisation de pas les espaces de noms, car j'ai l'impression que je n'ai pas vraiment d'adresse encore. Supposons que <Label> fait partie du même espace de noms que le reste du document, dans ce cas - urn:Abracadabra:

<MyTypeWithNamespaces>
    <Label>myLabel<Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

Votre constructeur de regarder comme il le ferait dans mon tout premier exemple de code, le long avec le public de la propriété 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 
// XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
// namespaces during serialization.
public MyTypeWithNamespaces( )
{
    this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
        new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
    });
}

[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces
{
    get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;

Puis, plus tard, dans votre code qui utilise l' MyTypeWithNamespaces objet à sérialiser, vous appelez ça comme je l'ai fait ci-dessus:

MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);

XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
    new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });

...

// Above, you'd setup your XmlTextWriter.

// Now serialize our object.
xs.Serialize(xtw, myType, myType.Namespaces);

Et l' XmlSerializer crache sur le même XML comme indiqué juste au-dessus sans ajout d'espaces de noms dans la sortie:

<MyTypeWithNamespaces>
    <Label>myLabel<Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

7voto

Cheeso Points 87022

Il y a une alternative, vous pouvez fournir à un membre de type XmlSerializerNamespaces dans le type à être sérialisé. Décorer avec l' XmlNamespaceDeclarations attribut. Ajouter les préfixes d'espace de noms et d'Uri pour ce membre. Puis, tout de sérialisation qui ne prévoit pas explicitement une XmlSerializerNamespaces utilisera le préfixe d'espace de noms+URI de paires que vous avez mis dans votre type.

Code d'exemple, supposons que c'est votre type:

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

Vous pouvez faire ceci:

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

Ce qui signifie que toute la sérialisation de cette instance qui ne précise pas son propre ensemble de préfixe+URI paires utilisera le "p" préfixe pour le "urn:monentreprise.2009" espace de noms. Il sera également omettre le xsi et xsd espaces de noms.

La différence ici est que vous ajoutez l'XmlSerializerNamespaces pour le type lui-même, plutôt que d'employer explicitement sur un appel à XmlSerializer.Serialize(). Cela signifie que si une instance de votre type est sérialisé par code vous ne possédez pas (par exemple dans un des services de la pile), et que le code ne prévoit pas explicitement un XmlSerializerNamespaces, que sérialiseur va utiliser les espaces de noms fournie dans l'instance.

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