Поскольку Дэйв попросил меня повторить мой ответ на вопрос « Пропуск всех пространств имен xsi и xsd при сериализации объекта в .NET» , я обновил этот пост и повторил свой ответ здесь из вышеупомянутой ссылки. В этом ответе использован тот же пример, что и для другого вопроса. Все, что следует ниже, скопировано дословно.
Прочитав документацию Microsoft и несколько решений в Интернете, я нашел решение этой проблемы. Он работает как со встроенной, так XmlSerializer
и с настраиваемой сериализацией XML через IXmlSerialiazble
.
Для начала я буду использовать тот же MyTypeWithNamespaces
образец XML, который до сих пор использовался в ответах на этот вопрос.
[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;
}
Это все для этого класса. Некоторые возражали против наличия XmlSerializerNamespaces
объекта где-нибудь в их классах; но, как видите, я аккуратно спрятал его в конструкторе по умолчанию и предоставил общедоступное свойство для возврата пространств имен.
Теперь, когда придет время сериализовать класс, вы должны использовать следующий код:
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);
Как только вы это сделаете, вы должны получить следующий результат:
<MyTypeWithNamespaces>
<Label xmlns="urn:Whoohoo">myLabel</Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
Я успешно использовал этот метод в недавнем проекте с глубокой иерархией классов, сериализованных в XML для вызовов веб-служб. В документации Microsoft не очень ясно, что делать с общедоступным XmlSerializerNamespaces
участником после его создания, и многие думают, что это бесполезно. Но, следуя их документации и используя ее описанным выше способом, вы можете настроить, как XmlSerializer генерирует XML для ваших классов, не прибегая к неподдерживаемому поведению или «собственной» сериализации путем реализации IXmlSerializable
.
Я надеюсь, что этот ответ раз и навсегда положит конец тому, как избавиться от стандартных пространств xsi
и xsd
пространств имен, сгенерированных XmlSerializer
.
ОБНОВЛЕНИЕ: я просто хочу убедиться, что ответил на вопрос OP об удалении всех пространств имен. Мой код выше подойдет для этого; позвольте мне показать вам, как это сделать. Теперь, в приведенном выше примере, вы действительно не можете избавиться от всех пространств имен (потому что используются два пространства имен). Где-то в вашем XML-документе вам понадобится что-то вроде xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo
. Если класс в примере является частью более крупного документа, то где-то выше должно быть объявлено пространство имен для одного из (или обоих) Abracadbra
и Whoohoo
. Если нет, то элемент в одном или обоих пространствах имен должен быть украшен каким-либо префиксом (у вас не может быть двух пространств имен по умолчанию, верно?). Итак, для этого примера Abracadabra
это пространство имен по умолчанию. Я мог бы внутри своего MyTypeWithNamespaces
класса добавить префикс пространства имен для Whoohoo
пространства имен следующим образом:
public MyTypeWithNamespaces
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:Abracadabra"), // Default Namespace
new XmlQualifiedName("w", "urn:Whoohoo")
});
}
Теперь в определении моего класса я указал, что <Label/>
элемент находится в пространстве имен "urn:Whoohoo"
, поэтому мне больше ничего не нужно делать. Когда я сериализую класс, используя приведенный выше код сериализации без изменений, вот результат:
<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
<w:Label>myLabel</w:Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
Поскольку он <Label>
находится в пространстве имен, отличном от остального документа, он должен каким-то образом быть «украшен» пространством имен. Обратите внимание , что до сих пор нет xsi
и xsd
пространств имен.
На этом мой ответ на другой вопрос заканчивается. Но я хотел убедиться, что я ответил на вопрос OP об использовании пространств имен, так как мне кажется, что я еще не рассмотрел этот вопрос. Предположим, что <Label>
это часть того же пространства имен, что и остальная часть документа, в этом случае urn:Abracadabra
:
<MyTypeWithNamespaces>
<Label>myLabel<Label>
<Epoch>42</Epoch>
</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[] {
new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
});
}
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces
{
get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;
Затем, позже, в вашем коде, который использует MyTypeWithNamespaces
объект для его сериализации, вы назовете его, как я сделал выше:
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);
И XmlSerializer
он выплюнул бы тот же XML, что показан непосредственно выше, без дополнительных пространств имен в выводе:
<MyTypeWithNamespaces>
<Label>myLabel<Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>