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.