XmlSerializer: удаление ненужных пространств имен xsi и xsd
есть ли способ настроить XmlSerializer так, чтобы он не писал пространства имен по умолчанию в корневом элементе?
что я получаю это:
<?xml ...>
<rootelement xmlns:xsi="..." xmlns:xsd="...">
</rootelement>
и я хочу удалить оба объявления xmlns.
дубликат:как сериализовать объект в XML без получения xmlns="..."?
3 ответов:
так как Дэйв попросил меня повторить мой ответ опуская все пространства имен 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.обновление: я просто хочу убедиться, что я ответил Вопрос ОП об удалении всех пространств имен. Мой код выше будет работать для этого; позвольте мне показать вам, как. Так вот, в приведенном выше примере, вы действительно не можете избавиться от всех пространств имен (потому что есть два пространства имен в использовании). Где-то в вашем 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>
//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)
есть альтернатива-вы можете предоставить член типа 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.Сериализовать.)( Это означает, что если экземпляр вашего типа сериализуется кодом, которым вы не владеете (например, в стеке веб-сервисов), и этот код явно не предоставляет XmlSerializerNamespaces, этот сериализатор будет использовать пространства имен, предоставленные в экземпляре.
Comments