XmlSerializer:删除不必要的xsi和xsd命名空间

问题描述 投票:129回答:4

有没有一种配置XmlSerializer的方法,以便它不会在根元素中写入默认名称空间?

我得到的是这个:

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

而且我想删除两个xmlns声明。

重复项How to serialize an object to XML without getting xmlns=”…”?

.net xml xml-serialization
4个回答
62
投票

[由于Dave要求我重复对Omitting all xsi and xsd namespaces when serializing an object in .NET的回答,所以我更新了此帖子,并从上述链接在此处重复了我的回答。此答案中使用的示例与其他问题使用的示例相同。逐字复制以下内容。


在在线阅读Microsoft的文档和几种解决方案后,我发现了解决此问题的方法。它与内置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");

******/
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成员后,Microsoft的文档尚不十分清楚,因此很多人认为它没有用。但是通过遵循他们的文档并以上面显示的方式使用它,您可以自定义XmlSerializer如何为您的类生成XML,而无需诉诸不受支持的行为或通过实现IXmlSerializable来“滚动自己的”序列化。

我希望这个答案能够一劳永逸地解决如何摆脱由xsi生成的标准xsdXmlSerializer命名空间。

更新:我只想确保我回答了OP有关删除所有名称空间的问题。我上面的代码将为此工作;让我告诉你怎么做。现在,在上面的示例中,您确实无法摆脱所有名称空间(因为正在使用两个名称空间)。在XML文档中的某处,您将需要具有xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo之类的内容。如果示例中的类是更大文档的一部分,则必须为AbracadbraWhoohoo之一(或两者)声明名称空间。如果不是,则必须使用某种前缀来装饰一个或两个名称空间中的元素(不能有两个默认名称空间,对吗?)。因此,在此示例中,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>与文档的其余部分位于不同的命名空间中,所以在某种程度上,它必须用命名空间“修饰”。请注意,仍然没有xsixsd命名空间。


到此为止,我对另一个问题的回答就结束了。但是我想确保我回答了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>

255
投票
//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)

6
投票

还有另一种选择-您可以在要序列化的类型中提供类型为XmlSerializerNamespaces的成员。用XmlNamespaceDeclarations属性装饰它。将名称空间前缀和URI添加到该成员。然后,任何未显式提供XmlSerializerNamespaces的序列化将使用您在类型中输入的名称空间前缀+ URI对。


0
投票

我正在使用:

© www.soinside.com 2019 - 2024. All rights reserved.