2017-12-18 375 views
1

我有一个类型,实现IXmlSerializable我正在序列化与DataContractSerializer。在将它作为XML文档的根元素进行序列化时,如何控制根元素名称空间和名称?如何在使用数据协定序列化器序列化IXmlSerializable对象时控制根元素名称空间和名称?

说我有以下类型:

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因为我的根对象,我得到:

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

我希望根域名为<Person>和根命名空间要"http://www.MyCompany.com",所以我尝试添加[DataContract]像这样:

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

但是,当我这样做,DataContractSerializer抛出一个异常,说明类型“PersonDTO”不能是IXmlSerializable的,并具有DataContractAttribute属性

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)构造函数修改根名称和命名空间当手动序列化:

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

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

回答

3

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

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

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

其中生成以下XML:

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

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

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

其次和更有力,可以使用[XmlSchemaProvider]属性来指定一个返回数据的静态方法合同名称,命名空间和模式的类型:

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

这样做的好处在于不仅将根名称和命名空间进行修改,而且在数组,泛型集合,以及其他仿制药使用的数据合同名称会以及:

<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将反映返回的模式。)
相关问题