由于 Dave 要求我重复我对 Omitting all xsi and xsd namespaces when serializing an object in .NET 的回答,我已经更新了这篇文章,并从上述链接中重复了我的回答。此答案中使用的示例与其他问题中使用的示例相同。以下内容逐字复制。
在网上阅读了微软的文档和几个解决方案后,我发现了这个问题的解决方案。它适用于内置的XmlSerializer 和通过IXmlSerialiazble 的自定义XML 序列化。
顺便说一句,我将使用迄今为止在此问题的答案中使用的相同 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");
******/
/*****
Per @dbc, since MyTypeWithNamespaces has a XmlRootAttribute decorating the class,
You may be able to get away with NOT using this .ctor and use the simple
XmlSerializer(Type) .ctor.
Also, be careful not to use serializer creation in loops, as it could lead
to extensive memory issues due to how serializers are cached (or not...).
See @dbc's comment and link to SO Q&A for more details.
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });
****/
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces));
// 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 成员,一旦你创建了它,很多人认为它没用。但是通过遵循他们的文档并以上面显示的方式使用它,您可以自定义 XmlSerializer 如何为您的类生成 XML,而无需诉诸不受支持的行为或通过实现 IXmlSerializable“滚动您自己的”序列化。
我希望这个答案能够彻底解决如何摆脱由XmlSerializer 生成的标准xsi 和xsd 命名空间。
更新:我只是想确保我回答了 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>