XmlSerializer:删除不必要的xsi和xsd命名空间
有没有办法configurationXmlSerializer,以便它不写入默认名称空间在根元素?
我得到的是这样的:
<?xml ...> <rootelement xmlns:xsi="..." xmlns:xsd="..."> </rootelement>
我想删除这两个xmlns声明。
重复 : 如何序列化一个对象到XML没有得到xmlns =“…”?
由于Dave要求我重复我的答案, 在.NET中序列化一个对象时忽略所有的xsi和xsd命名空间 ,所以我已经更新了这个post,并在上面的链接中重复了我的答案。 在这个答案中使用的例子是用于另一个问题的例子。 接下来是复制,逐字。
在网上阅读了微软的文档和几个解决scheme之后,我发现了解决这个问题的办法。 它可以通过IXmlSerialiazble
与内置的XmlSerializer
和自定义XML序列化配合使用。
MyTypeWithNamespaces
,我将使用迄今为止在这个问题的答案中使用的相同的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; }
这就是这个class。 现在,有人反对在他们的类中有一个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以用于Web服务调用。 微软的文档并不是很清楚如何处理公共可访问的XmlSerializerNamespaces
成员一旦创build它,许多人认为这是无用的。 但通过遵循他们的文档并以上面所示的方式使用它,您可以自定义XmlSerializer如何为您的类生成XML,而无需采取不支持的行为,或通过实现IXmlSerializable
“滚动自己的”序列化。
我希望这个答案能够xsi
摆脱XmlSerializer
生成的标准xsi
和xsd
命名空间。
更新:我只是想确保我回答了关于删除所有命名空间的OP的问题。 我上面的代码将为此工作; 让我告诉你如何。 现在,在上面的例子中,你真的不能摆脱所有的命名空间(因为有两个使用的命名空间)。 在你的XML文档的某个地方,你将需要像xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo
。如果示例中的类是大文档的一部分,那么在命名空间上方必须为Abracadbra
和Whoohoo
任何一个(或两者) Abracadbra
, Whoohoo
,一个或两个命名空间中的元素必须使用某种types的前缀进行修饰(不能有两个默认的命名空间,对不对?)因此,对于这个例子, Abracadabra
是默认的命名空间,我可以在MyTypeWithNamespaces
类中为Whoohoo
命名空间添加一个命名空间前缀,如下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相同的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)
还有一个select – 你可以在要序列化的types中提供一个types为XmlSerializerNamespaces的成员。 装饰它与XmlNamespaceDeclarations属性。 将名称空间前缀和URI添加到该成员。 那么,任何没有明确提供XmlSerializerNamespaces的序列化都将使用已经放入types的名称空间前缀+ URI对。
示例代码,假设这是你的types:
[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");
这将意味着,没有指定自己的一组前缀+ URI对的任何序列化将使用“urn:mycompany.2009”命名空间的“p”前缀。 它也将省略xsi和xsd命名空间。
这里的区别在于,您要将XmlSerializerNamespaces添加到types本身,而不是在调用XmlSerializer.Serialize()时显式使用它。 这意味着如果你的types的实例被你不拥有的代码序列化(例如在webservices栈中),并且代码没有明确地提供一个XmlSerializerNamespaces,那么这个序列化器将使用实例中提供的命名空间。