2009-04-17 2 views
114

Есть ли способ настроить XmlSerializer так, чтобы он не записывал пространства имен по умолчанию в корневом элементе?XmlSerializer: удалить ненужные пространства имен xsi и xsd

Что я получаю это:

<?xml ...> 
<rootelement xmlns:xsi="..." xmlns:xsd="..."> 
</rootelement> 

, и я хочу, чтобы удалить оба заявления Xmlns.

Дубликат: How to serialize an object to XML without getting xmlns=”…”?

ответ

51

Поскольку Дэйв попросил меня повторить мой ответ 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> 
+0

Для полноты, возможно, вы должны включить здесь правильный ответ, а не просто ссылаться на него, а также мне интересно узнать, как вы заключаете, что это «неподдерживаемое поведение». –

+0

Хорошо, поэтому здесь сказано так: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializernamespaces(v=vs.100).aspx –

+0

Дэйв, я повторил свой ответ за вас , Кроме того, я хотел удостовериться, что я обратился к вашему первоначальному вопросу о том, что ни одно пространство имен не появляется. Если у вас есть только одно пространство имен в вашем XML-фрагменте/документе, и вы добавили пространство имен по умолчанию в общедоступную коллекцию 'XmlSerializerNamespaces' в вашем объекте со всеми элементами, принадлежащими этому пространству имен, то при вызове' XmlSerializer', как я показал выше, no пространства имен будут выводиться в результате serialzied XML. НТН. – fourpastmidnight

226
//Create our own namespaces for the output 
XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 

//Add an empty namespace and empty value 
ns.Add("", ""); 

//Create the serializer 
XmlSerializer slz = new XmlSerializer(someType); 

//Serialize the object with our own namespaces (notice the overload) 
slz.Serialize(myXmlTextWriter, someObject, ns) 
+2

Ровно. Работает как шарм. –

+21

Хммм ... вы, ребята, мятежники. Он явно говорит на http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializernamespaces(v=vs.100).aspx, что вы не можете этого сделать. –

+0

Bool Yah! (Для того, чтобы преодолеть то, что говорят ms, вы не можете сделать) – granadaCoder

6

Существует альтернатива - вы можете предоставить элемент типа XmlSerializerNamespaces в типе сериализовать. Украсьте его атрибутом XmlNamespaceDeclarations. Добавьте к этому члену префиксы пространства имен и URI. Затем любая сериализация, которая явно не предоставляет пространство XmlSerializerNamespaces, будет использовать пары префиксов пространства имен + пары URI, которые вы поместили в свой тип.

Пример кода, предположим, что это ваш тип:

[XmlRoot(Namespace = "urn:mycompany.2009")] 
public class Person { 
    [XmlAttribute] 
    public bool Known; 
    [XmlElement] 
    public string Name; 
    [XmlNamespaceDeclarations] 
    public XmlSerializerNamespaces xmlns; 
} 

Вы можете сделать это:

var p = new Person 
    { 
     Name = "Charley", 
     Known = false, 
     xmlns = new XmlSerializerNamespaces() 
    } 
p.xmlns.Add("",""); // default namespace is emoty 
p.xmlns.Add("c", "urn:mycompany.2009"); 

И это будет означать, что любой сериализации этого экземпляра, который не определяет свой собственный набор prefix + URI будут использовать префикс «p» для пространства имен «urn: mycompany.2009». Он также опустит пространства имен xsi и xsd.

Разница заключается в том, что вы добавляете пространство XmlSerializerNamespaces к самому типу, вместо того, чтобы явно использовать его при вызове XmlSerializer.Serialize(). Это означает, что если экземпляр вашего типа сериализуется кодом, которым вы не являетесь (например, в стеке webservices), и этот код явно не предоставляет пространство XmlSerializerNamespaces, этот сериализатор будет использовать пространства имен, предоставленные в экземпляре.

+0

1. Я не вижу разницы. 'все еще добавляет пространство имен по умолчанию к экземпляру XmlSerializerNamespaces. –

+2

2. Это загрязняет классы больше. Моя цель - не использовать определенное пространство имен, моя цель - не использовать пространства имен вообще. –

+0

Я добавил примечание о различии между этим подходом и описанием пространства XmlSerializerNamespaces во время сериализации. – Cheeso

Смежные вопросы