2017-12-18 147 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將反映返回的模式。)
相關問題