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

我得到的是:

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

我想删除这两个xmlns声明.

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

推荐答案

自从Dave让我重复我的答案到Omitting all xsi and xsd namespaces when serializing an object in .NET,我已经更新了这篇帖子,并在上面提到的链接中重复了我的答案.这个答案中使用的例子与另一个问题中使用的例子相同.下面是逐字复制的内容.


在网上阅读了微软的文档和几个解决方案后,我发现了这个问题的解决方案.它通过IXmlSerialiazble与内置XmlSerializer和自定义XML序列化一起工作.

简而言之,我将使用到目前为止在回答这个问题时使用的相同的MyTypeWithNamespacesXML示例.

[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>

在最近的一个项目中,我成功地使用了此方法,其中的类被序列化为用于Web服务调用的XML的深层层次结构.微软的文档不是很清楚,一旦你创建了可公开访问的XmlSerializerNamespaces个成员,该怎么做,所以很多人认为它是无用的.但是,通过遵循他们的文档并以上面所示的方式使用它,您可以定制XmlSerializer如何为您的类生成XML,而无需诉诸不受支持的行为或通过实现IXmlSerializable来"滚动"您自己的序列化.

我希望这个答案将一劳永逸地解决如何摆脱XmlSerializer生成的标准xsixsd名称空间的问题.

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

.net相关问答推荐

如何在 .Net Core EF 中组合多个条件表达式来过滤数据?

在接口内部声明 IEnumerable 而在具体类中声明 IList

out 和 ref 可以用作临时变量吗?

使属性只能通过绑定的 Editor(component) 编辑

无法实例化类的代理:System.Net.HttpWebRequest.找不到无参数构造函数

双精度的 C++ 和 C# 十六进制值之间的差异

作者主签名的时间戳发现了一个建链问题:UntrustedRoot: self-signed certificate in certificate chain

如何为多种文件类型设置 FileSystemWatcher 过滤器?

什么取代了 .Net Core 中的 WCF?

.NET Framework SDK 和 Targeting 包有什么区别

软件包版本始终为 1.0.0,带有 dotnet pack

为什么 ?: 会导致转换错误,而 if-else 不会?

Nuget 连接try 失败“无法加载源的服务索引”

为什么无穷大在 Windows 10 控制台中打印为“8”?

找不到库 hostpolicy.dll

如何卸载“Microsoft .NET Core 1.0.0 RC2 - VS 2015 Tooling Preview 1”?

为什么这个字符串的长度比它的字符数长?

创建一个已完成的任务

为什么 HttpClient BaseAddress 不起作用?

Microsoft.Bcl.Build NuGet 包有什么作用?