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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/760262/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 12:32:14  来源:igfitidea点击:

XmlSerializer: remove unnecessary xsi and xsd namespaces

.netxmlxml-serialization

提问by Dave Van den Eynde

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

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

What I get is this:

我得到的是这样的:

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

and I want to remove both xmlns declarations.

我想删除两个 xmlns 声明。

Duplicate of: How to serialize an object to XML without getting xmlns=”…”?

重复如何在不获取 xmlns=”...” 的情况下将对象序列化为 XML?

采纳答案by fourpastmidnight

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.

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



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

网上看了微软的文档和几个解决方案,找到了解决这个问题的办法。它XmlSerializer通过IXmlSerialiazble.

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

简而言之,我将使用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;
}

That's all to this class. Now, some objected to having an XmlSerializerNamespacesobject 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.

这就是这门课的全部内容。现在,有些人反对XmlSerializerNamespaces在他们的类中的某个地方有一个对象;但正如你所看到的,我巧妙地将它隐藏在默认构造函数中,并公开了一个公共属性来返回命名空间。

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

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

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

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

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

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 XmlSerializerNamespacesmember 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.

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

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

我希望这个答案会平息,一劳永逸,如何摆脱标准的xsi,并xsd通过所产生的命名空间XmlSerializer

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) Abracadbraand 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, Abracadabrais the default namespace. I could inside my MyTypeWithNamespacesclass add a namespace prefix for the Whoohoonamespace like so:

更新:我只是想确保我回答了 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")
    });
}

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:

现在,在我的类定义中,我指出该<Label/>元素在 namespace 中"urn:Whoohoo",所以我不需要再做任何事情了。当我现在使用上面的序列化代码不变地序列化类时,这是输出:

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

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 xsiand xsdnamespaces.

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



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:

我对另一个问题的回答到此结束。但我想确保我回答了 OP 关于不使用命名空间的问题,因为我觉得我还没有真正解决它。假设它<Label>与文档的其余部分属于同一命名空间,在这种情况下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;

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

然后,稍后,在使用该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);

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

并且XmlSerializer会吐出与上面显示的相同的 XML,输出中没有额外的命名空间:

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

回答by Jeremy

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

回答by Cheeso

There is an alternative - you can provide a member of type XmlSerializerNamespacesin the type to be serialized. Decorate it with the XmlNamespaceDeclarationsattribute. Add the namespace prefixes and URIs to that member. Then, any serialization that does not explicitly provide an XmlSerializerNamespaces will use the namespace prefix+URI pairs you have put into your type.

还有一种替代方法 - 您可以在要序列化的类型中提供XmlSerializerNamespaces类型的成员。使用XmlNamespaceDeclarations属性装饰它。向该成员添加命名空间前缀和 URI。然后,任何未显式提供 XmlSerializerNamespaces 的序列化都将使用您已放入类型中的命名空间前缀 + URI 对。

Example code, suppose this is your type:

示例代码,假设这是您的类型:

[XmlRoot(Namespace = "urn:mycompany.2009")]
public class Person {
  [XmlAttribute] 
  public bool Known;
  [XmlElement]
  public string Name;
  [XmlNamespaceDeclarations]
  public XmlSerializerNamespaces xmlns;
}

You can do this:

你可以这样做:

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

And that will mean that any serialization of that instance that does not specify its own set of prefix+URI pairs will use the "p" prefix for the "urn:mycompany.2009" namespace. It will also omit the xsi and xsd namespaces.

这意味着该实例的任何序列化如果没有指定自己的前缀+URI 对集,将使用“p”前缀作为“urn:mycompany.2009”命名空间。它还将省略 xsi 和 xsd 命名空间。

The difference here is that you are adding the XmlSerializerNamespaces to the type itself, rather than employing it explicitly on a call to XmlSerializer.Serialize(). This means that if an instance of your type is serialized by code you do not own (for example in a webservices stack), and that code does not explicitly provide a XmlSerializerNamespaces, that serializer will use the namespaces provided in the instance.

这里的区别在于您将 XmlSerializerNamespaces 添加到类型本身,而不是在调用 XmlSerializer.Serialize() 时显式使用它。这意味着,如果您的类型的实例由您不拥有的代码序列化(例如在 Web 服务堆栈中),并且该代码未显式提供 XmlSerializerNamespaces,则该序列化程序将使用实例中提供的命名空间。

回答by Maxence

I'm using:

我正在使用:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        const string DEFAULT_NAMESPACE = "http://www.something.org/schema";
        var serializer = new XmlSerializer(typeof(Person), DEFAULT_NAMESPACE);
        var namespaces = new XmlSerializerNamespaces();
        namespaces.Add("", DEFAULT_NAMESPACE);

        using (var stream = new MemoryStream())
        {
            var someone = new Person
            {
                FirstName = "Donald",
                LastName = "Duck"
            };
            serializer.Serialize(stream, someone, namespaces);
            stream.Position = 0;
            using (var reader = new StreamReader(stream))
            {
                Console.WriteLine(reader.ReadToEnd());
            }
        }
    }
}

To get the following XML:

要获取以下 XML:

<?xml version="1.0"?>
<Person xmlns="http://www.something.org/schema">
  <FirstName>Donald</FirstName>
  <LastName>Duck</LastName>
</Person>

If you don't want the namespace, just set DEFAULT_NAMESPACE to "".

如果您不想要命名空间,只需将 DEFAULT_NAMESPACE 设置为“”。