Поскольку Дэйв попросил меня повторить мой ответ Omitting all xsi and xsd namespaces when serializing an object in .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>
I успешно использовали этот метод в недавнем проекте с глубокой иерархией классов, которые сериализованы для XML для вызовов веб-сервисов. Документация Microsoft не совсем понятна, что делать с публично доступным участником XmlSerializerNamespaces
, как только вы ее создали, и многие считают, что это бесполезно. Но, следуя их документации и используя ее в порядке, показанном выше, вы можете настроить, как XmlSerializer генерирует XML для ваших классов, не прибегая к неподдерживаемому поведению или «сворачиванию собственной» сериализации путем реализации IXmlSerializable
.
Надеюсь, что этот ответ положит конец раз и навсегда, как избавиться от стандартных xsi
и xsd
пространств имен, созданных XmlSerializer
.
UPDATE: Я просто хочу убедиться, что я ответил на вопрос 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>
Для полноты, возможно, вы должны включить здесь правильный ответ, а не просто ссылаться на него, а также мне интересно узнать, как вы заключаете, что это «неподдерживаемое поведение». –
Хорошо, поэтому здесь сказано так: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializernamespaces(v=vs.100).aspx –
Дэйв, я повторил свой ответ за вас , Кроме того, я хотел удостовериться, что я обратился к вашему первоначальному вопросу о том, что ни одно пространство имен не появляется. Если у вас есть только одно пространство имен в вашем XML-фрагменте/документе, и вы добавили пространство имен по умолчанию в общедоступную коллекцию 'XmlSerializerNamespaces' в вашем объекте со всеми элементами, принадлежащими этому пространству имен, то при вызове' XmlSerializer', как я показал выше, no пространства имен будут выводиться в результате serialzied XML. НТН. – fourpastmidnight