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 objet dans .NET , j'ai mis à jour ce post et répété ma réponse ici à partir du lien susmentionné. L'exemple utilisé dans cette réponse est le même 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 avec la XmlSerializer
sérialisation XML intégrée et personnalisée via IXmlSerialiazble
.
Pour tout dire, j'utiliserai le même MyTypeWithNamespaces
exemple XML 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 se sont opposés à avoir un XmlSerializerNamespaces
objet 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 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 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 XmlSerializerNamespaces
membre 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 illustrée ci-dessus, vous pouvez personnaliser la façon dont XmlSerializer génère du XML pour vos classes sans recourir à un comportement non pris en charge ou "lancer votre propre" sérialisation en implémentant IXmlSerializable
.
J'espère que cette réponse mettra fin, une fois pour toutes, à la manière de se débarrasser du standard xsi
et des xsd
espaces de noms générés par le XmlSerializer
.
MISE À JOUR: Je veux juste m'assurer d'avoir répondu à la question du PO sur 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 en cours d'utilisation). Quelque part dans votre document XML, vous devrez avoir quelque chose comme xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo
. Si la classe de l'exemple fait partie d'un document plus volumineux, alors quelque part au-dessus d'un espace de noms doit être déclaré pour l'un des (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, n'est-ce pas?). Donc, pour cet exemple, Abracadabra
est l'espace de noms par défaut. Je pourrais dans ma MyTypeWithNamespaces
classe ajouter un préfixe d'espace de noms pour l' Whoohoo
espace de noms comme ceci:
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 classe, j'ai indiqué que l' <Label/>
élément est dans l'espace de noms "urn:Whoohoo"
, donc je n'ai rien à faire de plus. Lorsque je sérialise maintenant la classe en utilisant mon code de sérialisation ci-dessus inchangé, voici le résultat:
<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
<w:Label>myLabel</w:Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
Étant donné qu'il se <Label>
trouve dans un espace de noms différent du reste du document, il doit, en quelque sorte, être «décoré» d'un espace de noms. Notez qu'il n'y a toujours pas d' espaces de noms xsi
et xsd
.
Ceci termine ma réponse à l'autre question. Mais je voulais m'assurer de répondre à la question du PO sur l'utilisation d'aucun espace de noms, car je pense que je ne l'ai pas encore vraiment abordé. Supposons que cela <Label>
fasse 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 ressemblerait à ce qu'il serait dans mon tout 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
// 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;
Ensuite, plus tard, dans votre code qui utilise l' MyTypeWithNamespaces
objet pour le sérialiser, vous l'appelleriez 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 le XmlSerializer
cracherait le même XML que celui indiqué immédiatement ci-dessus sans espaces de noms supplémentaires dans la sortie:
<MyTypeWithNamespaces>
<Label>myLabel<Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);
j'ai dû remplacer parvar xtw = XmlTextWriter.Create(memStm, xws);
.XmlTextWriter.Create
renvoie uneXmlWriter
instance (abstraite?) . Donc @ Preza8 est correct, vous perdriez la possibilité de définir d'autresXmlTextWriter
propriétés spécifiques (du moins, pas sans la réduire), d'où la conversion spécifique enXmlTextWriter
.la source
Il existe une alternative - vous pouvez fournir un membre de type XmlSerializerNamespaces dans le type à sérialiser. Décorez-le avec l' attribut 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 URI que vous avez placées dans votre type.
Exemple de code, supposons que ce soit votre type:
Tu peux le faire:
Et cela signifie que toute sérialisation de cette instance qui ne spécifie pas son propre ensemble de paires préfixe + 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 les 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 un code que vous ne possédez pas (par exemple dans une pile 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.
la source
J'utilise:
Pour obtenir le XML suivant:
Si vous ne voulez pas d'espace de noms, définissez simplement DEFAULT_NAMESPACE sur "".
la source