使用数据协定序列化程序序列化 IXmlSerializable 对象时,如何控制根元素命名空间和名称? [英] How can I control the root element namespace and name when serializing an IXmlSerializable object with the data contract serializer?

查看:19
本文介绍了使用数据协定序列化程序序列化 IXmlSerializable 对象时,如何控制根元素命名空间和名称?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类型实现 IXmlSerializable 我正在使用 DataContractSerializer.将根元素序列化为 XML 文档的根元素时,如何控制根元素命名空间和名称?

I have a type that implements IXmlSerializable which I am serializing with DataContractSerializer. How can I control the root element namespace and name when serializing it as the root element of an XML document?

假设我有以下类型:

public partial class PersonDTO : IXmlSerializable
{
    public string Name { get; set; }

    #region IXmlSerializable Members

    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        Name = reader["name"];
        if (!reader.IsEmptyElement)
            reader.Skip();
        reader.Read();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        writer.WriteAttributeString("name", Name);
    }

    #endregion
}

如果我使用 DataContractSerializer 作为我的根对象序列化它,我得到:

If I serialize this with DataContractSerializer as my root object I get:

<PersonDTO name="John Doe" xmlns="http://schemas.datacontract.org/2004/07/MyClrNamespace" />

我希望根名称为 ,根名称空间为 "http://www.MyCompany.com",所以我尝试添加[DataContract] 像这样:

I want the root name to be <Person> and the root namespace to be "http://www.MyCompany.com", so I tried adding [DataContract] like so:

[DataContract(Name = "Person", Namespace = "http://www.MyCompany.com")]
public partial class PersonDTO : IXmlSerializable
{
}

但是当我这样做时,DataContractSerializer 会抛出一个异常,指出 Type 'PersonDTO' 不能是 IXmlSerializable 并且具有 DataContractAttribute 属性:

But when I do, DataContractSerializer throws an exception stating Type 'PersonDTO' cannot be IXmlSerializable and have DataContractAttribute attribute:

System.Runtime.Serialization.InvalidDataContractException occurred
  Message="Type 'PersonDTO' cannot be IXmlSerializable and have DataContractAttribute attribute."
  Source="System.Runtime.Serialization"
  StackTrace:
       at System.Runtime.Serialization.XmlDataContract.XmlDataContractCriticalHelper..ctor(Type type)
       at System.Runtime.Serialization.XmlDataContract..ctor(Type type)
       at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type)
       at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type)
       at System.Runtime.Serialization.DataContract.GetDataContract(RuntimeTypeHandle typeHandle, Type type, SerializationMode mode)
       at System.Runtime.Serialization.DataContractSerializer.get_RootContract()

我知道可以使用 修改根名称和命名空间DataContractSerializer(Type type, String rootName, String rootNamespace) 手动序列化时的构造函数:

I know it is possible to modify the root name and namespace by using the DataContractSerializer(Type type, String rootName, String rootNamespace) constructor when serializing manually:

var person = new PersonDTO { Name = "John Doe", };

var serializer = new DataContractSerializer(typeof(PersonDTO), "Person", @"http://www.MyCompany.com");
var sb = new StringBuilder();
using (var textWriter = new StringWriter(sb))
using (var xmlWriter = XmlWriter.Create(textWriter))
{
    serializer.WriteObject(xmlWriter, person);
}
Console.WriteLine(sb);
// Outputs <Person name="John Doe" xmlns="http://www.MyCompany.com" />

但是有没有办法通过属性自动做到这一点?

But is there any way to do this automatically via attributes?

推荐答案

这可以通过以下两种方式之一使用属性来完成.

This can be done using attributes in one of two ways.

首先(并且令人惊讶地)如果您应用 [XmlRoot] 属性为旧的 XmlSerializer 类型,DataContractSerializer 将使用命名空间和其中指定的名称作为根数据协定命名空间和名称:

Firstly (and surprisingly) if you apply the [XmlRoot] attribute for the old XmlSerializer to the type, DataContractSerializer will use the namespace and name specified therein as the root data contract namespace and name:

[XmlRoot("Person", Namespace = "http://www.MyCompany.com")]
public partial class PersonDTO : IXmlSerializable
{
}

生成以下 XML:

<Person name="John Doe" xmlns="http://www.MyCompany.com" />

但是,此解决方案仅适用于根元素名称.如果您尝试序列化此类对象的数组或通用列表,则使用未修改的命名空间和名称:

However, this solution only applies to the root element name. If you try to serialize an array or generic list of such objects the unmodified namespace and name are used:

<ArrayOfPersonDTO xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/MyClrNamespace">
  <PersonDTO name="John Doe" />
</ArrayOfPersonDTO>

其次,更强大的是[XmlSchemaProvider] 属性可用于指定返回类型的数据协定名称、命名空间和架构的静态方法:

Secondly and more powerfully, the [XmlSchemaProvider] attribute can be used to specify a static method that returns a data contract name, namespace and schema for the type:

[XmlSchemaProvider("GetSchemaMethod")]
public partial class PersonDTO : IXmlSerializable
{
    // This is the method named by the XmlSchemaProviderAttribute applied to the type.
    public static XmlQualifiedName GetSchemaMethod(XmlSchemaSet xs)
    {
        // Fill in a plausible schema for the type if necessary.
        // 
        // While DataContractSerializer will not use the returned schema set, 
        // svcutil.exe will use it to generate schemas.  XmlSerializer also
        // seems to require it to be initialized to something plausible if you
        // are serializing your types with both serializers.
        string personSchema = @"<xs:schema xmlns:tns=""http://www.MyCompany.com"" elementFormDefault=""qualified"" targetNamespace=""http://www.MyCompany.com"" xmlns:xs=""http://www.w3.org/2001/XMLSchema"">
  <xs:element name=""Person"" nillable=""true"" type=""tns:Person"" />
  <xs:complexType name=""Person"">
    <xs:attribute name=""name"" type=""xs:string"" />
  </xs:complexType>
</xs:schema>";
        using (var textReader = new StringReader(personSchema))
        using (var schemaSetReader = System.Xml.XmlReader.Create(textReader))
        {
            xs.Add("http://www.MyCompany.com", schemaSetReader);
        }
        // Return back the namespace and name to be used for this type.
        return new XmlQualifiedName("Person", "http://www.MyCompany.com");
    }
}

这样做的好处是,不仅根名称和命名空间会被修改,数组、泛型集合和其他泛型中使用的数据契约名称也会被修改:

This has the advantage that not only will the root name and namespace be modified, but also the data contract name used in arrays, generic collections, and other generics will be as well:

<ArrayOfPerson xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.MyCompany.com">
  <Person name="John Doe" />
</ArrayOfPerson>

注意事项:

  • DataContractSerializer 仅使用架构提供程序方法返回的 XmlQualifiedName.但是,如果您计划使用 svcutil.exe 为您的类型生成 XSD 或还使用 XmlSerializer 序列化您的类型,则需要填写 XmlSchemaSet xs 有一些合理的东西.(当您这样做时,生成的 XSD 将反映返回的架构.)
  • DataContractSerializer uses only the XmlQualifiedName returned by the schema provider method. However, if you plan to generate an XSD for your type using svcutil.exe or also serialize your type with XmlSerializer, you will need to fill in the XmlSchemaSet xs with something plausible. (And when you do, the XSD generated will reflect the returned schema.)

这篇关于使用数据协定序列化程序序列化 IXmlSerializable 对象时,如何控制根元素命名空间和名称?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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