Nachdem ich die Dokumentation von Microsoft und verschiedene Lösungen online gelesen habe, habe ich die Lösung für dieses Problem gefunden. Es funktioniert sowohl mit der integrierten XmlSerializer
als auch mit der benutzerdefinierten XML-Serialisierung über IXmlSerialiazble
.
Ich werde also dasselbe MyTypeWithNamespaces
XML-Beispiel verwenden, das bisher in den Antworten auf diese Frage verwendet wurde.
[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;
}
Das ist alles für diese Klasse. Nun hatten einige Einwände dagegen, XmlSerializerNamespaces
irgendwo in ihren Klassen ein Objekt zu haben; Aber wie Sie sehen, habe ich es ordentlich in den Standardkonstruktor gesteckt und eine öffentliche Eigenschaft verfügbar gemacht, um die Namespaces zurückzugeben.
Wenn es Zeit ist, die Klasse zu serialisieren, verwenden Sie den folgenden Code:
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);
Sobald Sie dies getan haben, sollten Sie die folgende Ausgabe erhalten:
<MyTypeWithNamespaces>
<Label xmlns="urn:Whoohoo">myLabel</Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
Ich habe diese Methode kürzlich in einem Projekt mit einer tiefen Hierarchie von Klassen erfolgreich verwendet, die für Webdienstaufrufe in XML serialisiert werden. In der Dokumentation von Microsoft ist nicht klar, was mit dem öffentlich zugänglichen XmlSerializerNamespaces
Mitglied zu tun ist, wenn Sie es erstellt haben, und so viele halten es für nutzlos. Wenn Sie jedoch der Dokumentation folgen und sie auf die oben gezeigte Weise verwenden, können Sie anpassen, wie der XmlSerializer XML für Ihre Klassen generiert, ohne auf nicht unterstütztes Verhalten zurückgreifen oder Ihre eigene Serialisierung durch Implementierung "rollen" zu lassen IXmlSerializable
.
Ich hoffe, dass diese Antwort ein für alle Mal zum Erliegen kommt, wie der Standard xsi
und die xsd
Namespaces, die von der XmlSerializer
.
UPDATE: Ich möchte nur sicherstellen, dass ich die Frage des OP zum Entfernen aller Namespaces beantwortet habe. Mein Code oben wird dafür funktionieren; lass mich dir zeigen wie. Im obigen Beispiel können Sie wirklich nicht alle Namespaces entfernen (da zwei Namespaces verwendet werden). Irgendwo in Ihrem XML-Dokument benötigen Sie so etwas wie xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo
. Wenn die Klasse im Beispiel Teil eines größeren Dokuments ist, muss irgendwo über einem Namespace für eines von (oder für beide) Abracadbra
und deklariert werden Whoohoo
. Wenn nicht, muss das Element in einem oder beiden Namespaces mit einem Präfix versehen sein (Sie können nicht zwei Standard-Namespaces haben, oder?). In diesem Beispiel Abracadabra
handelt es sich also um den Defalt-Namespace. Ich könnte in meiner MyTypeWithNamespaces
Klasse ein Namespace-Präfix für den Whoohoo
Namespace wie folgt hinzufügen :
public MyTypeWithNamespaces
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:Abracadabra"), // Default Namespace
new XmlQualifiedName("w", "urn:Whoohoo")
});
}
In meiner Klassendefinition habe ich nun angegeben, dass sich das <Label/>
Element im Namespace befindet "urn:Whoohoo"
, sodass ich nichts weiter tun muss. Wenn ich jetzt die Klasse mit meinem obigen Serialisierungscode unverändert serialisiere, ist dies die Ausgabe:
<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
<w:Label>myLabel</w:Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
Da <Label>
es sich in einem anderen Namespace als der Rest des Dokuments befindet, muss es in gewisser Weise mit einem Namespace "dekoriert" werden. Beachten Sie, dass es noch keine sind xsi
und xsd
Namensräume.