XmlSerializer:删除不必要的 xsi 和 xsd 命名空间 [英] XmlSerializer: remove unnecessary xsi and xsd namespaces

查看:36
本文介绍了XmlSerializer:删除不必要的 xsi 和 xsd 命名空间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有办法配置 XmlSerializer 使其不在根元素中写入默认命名空间?

Is there a way to configure the XmlSerializer so that it doesn't write default namespaces in the root element?

我得到的是这个:

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

我想删除两个 xmlns 声明.

and I want to remove both xmlns declarations.

重复:如何将对象序列化为 XML 而不获取 xmlns="..."?

推荐答案

因为 Dave 要我重复我对 省略所有 xsi 的回答在 .NET 中序列化对象时使用和 xsd 命名空间,我更新了这篇文章,并从上述链接中重复了我的回答.此答案中使用的示例与其他问题中使用的示例相同.以下内容逐字复制.

Since Dave asked for me to repeat my answer to Omitting all xsi and xsd namespaces when serializing an object in .NET, I have updated this post and repeated my answer here from the afore-mentioned link. The example used in this answer is the same example used for the other question. What follows is copied, verbatim.

在网上看了微软的文档和几个解决方案,找到了解决这个问题的办法.它适用于内置的 XmlSerializer 和通过 IXmlSerialiazble 的自定义 XML 序列化.

After reading Microsoft's documentation and several solutions online, I have discovered the solution to this problem. It works with both the built-in XmlSerializer and custom XML serialization via IXmlSerialiazble.

简而言之,我将使用迄今为止在此问题的答案中使用的相同 MyTypeWithNamespaces XML 示例.

To whit, I'll use the same MyTypeWithNamespaces XML sample that's been used in the answers to this question so far.

[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 对象;但正如你所看到的,我巧妙地将它隐藏在默认构造函数中,并公开了一个公共属性来返回命名空间.

That's all to this class. Now, some objected to having an XmlSerializerNamespaces object somewhere within their classes; but as you can see, I neatly tucked it away in the default constructor and exposed a public property to return the namespaces.

现在,当需要序列化类时,您将使用以下代码:

Now, when it comes time to serialize the class, you would use the following code:

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);

完成此操作后,您应该得到以下输出:

Once you have done this, you should get the following output:

<MyTypeWithNamespaces>
    <Label xmlns="urn:Whoohoo">myLabel</Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

我在最近的一个项目中成功地使用了这种方法,该项目具有深层次的类,这些类被序列化为 XML 以进行 Web 服务调用.Microsoft 的文档并不清楚一旦您创建了可公开访问的 XmlSerializerNamespaces 成员如何处理它,因此很多人认为它没有用.但是通过遵循他们的文档并以上面显示的方式使用它,您可以自定义 XmlSerializer 如何为您的类生成 XML,而无需求助于不受支持的行为或滚动您自己的".通过实现IXmlSerializable进行序列化.

I have successfully used this method in a recent project with a deep hierachy of classes that are serialized to XML for web service calls. Microsoft's documentation is not very clear about what to do with the publicly accesible XmlSerializerNamespaces member once you've created it, and so many think it's useless. But by following their documentation and using it in the manner shown above, you can customize how the XmlSerializer generates XML for your classes without resorting to unsupported behavior or "rolling your own" serialization by implementing IXmlSerializable.

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

It is my hope that this answer will put to rest, once and for all, how to get rid of the standard xsi and xsd namespaces generated by the XmlSerializer.

更新:我只是想确保我回答了 OP 关于删除所有命名空间的问题.我上面的代码适用于此;让我来告诉你怎么做.现在,在上面的示例中,您确实无法摆脱所有命名空间(因为有两个命名空间正在使用).在 XML 文档的某处,您将需要有类似 xmlns=urn:Abracadabra"之类的内容.xmlns:w="urn:Whoohoo.如果示例中的类是较大文档的一部分,则必须为 AbracadbraWhoohoo 之一(或两者)声明命名空间上方的某处.如果没有,那么一个或两个命名空间中的元素必须用某种前缀装饰(你不能有两个默认命名空间,对吧?).因此,对于此示例,Abracadabra 是默认命名空间.我可以在我的 MyTypeWithNamespaces 类中为 Whoohoo 命名空间添加命名空间前缀,如下所示:

UPDATE: I just want to make sure I answered the OP's question about removing all namespaces. My code above will work for this; let me show you how. Now, in the example above, you really can't get rid of all namespaces (because there are two namespaces in use). Somewhere in your XML document, you're going to need to have something like xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo. If the class in the example is part of a larger document, then somewhere above a namespace must be declared for either one of (or both) Abracadbra and Whoohoo. If not, then the element in one or both of the namespaces must be decorated with a prefix of some sort (you can't have two default namespaces, right?). So, for this example, Abracadabra is the default namespace. I could inside my MyTypeWithNamespaces class add a namespace prefix for the Whoohoo namespace like so:

public MyTypeWithNamespaces
{
    this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
        new XmlQualifiedName(string.Empty, "urn:Abracadabra"), // Default Namespace
        new XmlQualifiedName("w", "urn:Whoohoo")
    });
}

现在,在我的类定义中,我指出 元素位于名称空间 "urn:Whoohoo" 中,所以我不不需要做任何进一步的事情.当我现在使用上面的序列化代码不变地序列化类时,这是输出:

Now, in my class definition, I indicated that the <Label/> element is in the namespace "urn:Whoohoo", so I don't need to do anything further. When I now serialize the class using my above serialization code unchanged, this is the output:

<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
    <w:Label>myLabel</w:Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

因为 <Label> 与文档的其余部分位于不同的命名空间中,所以它必须以某种方式进行装饰".带有命名空间.请注意,仍然没有 xsixsd 命名空间.

Because <Label> is in a different namespace from the rest of the document, it must, in someway, be "decorated" with a namespace. Notice that there are still no xsi and xsd namespaces.

我对另一个问题的回答到此结束.但我想确保我回答了 OP 关于不使用命名空间的问题,因为我觉得我还没有真正解决它.假设 与文档的其余部分属于同一命名空间,在本例中为 urn:Abracadabra:

This ends my answer to the other question. But I wanted to make sure I answered the OP's question about using no namespaces, as I feel I didn't really address it yet. Assume that <Label> is part of the same namespace as the rest of the document, in this case urn:Abracadabra:

<MyTypeWithNamespaces>
    <Label>myLabel<Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

您的构造函数看起来就像在我的第一个代码示例中一样,以及用于检索默认命名空间的公共属性:

Your constructor would look as it would in my very first code example, along with the public property to retrieve the default namespace:

// 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 对象对其进行序列化的代码中,您可以像我上面那样调用它:

Then, later, in your code that uses the MyTypeWithNamespaces object to serialize it, you would call it as I did above:

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,输出中没有额外的命名空间:

And the XmlSerializer would spit back out the same XML as shown immediately above with no additional namespaces in the output:

<MyTypeWithNamespaces>
    <Label>myLabel<Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

这篇关于XmlSerializer:删除不必要的 xsi 和 xsd 命名空间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆