在使用数据协定序列化程序序列化IXmlSerializable对象时,如何控制根元素名称空间和名称?

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

说我有以下类型:

 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作为我的根对象将其序列化,我得到:

  

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

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

但是当我这样做时, DataContractSerializer抛出一个exception,说明类型’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  

但有没有办法通过属性自动执行此操作?

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

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

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

这会生成以下XML:

  

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

    

其次 ,更强大的是, [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 = @"     "; 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"); } } 

这样做的好处是,不仅可以修改根名称和命名空间,还可以使用数组,generics集合和其他generics中使用的数据协定名称:

    

笔记:

  • DataContractSerializer仅使用架构提供程序方法返回的XmlQualifiedName 。 但是,如果您计划使用svcutil.exe为您的类型生成XSD,或者还使用XmlSerializer序列化您的类型,则需要使用合理的东西填充XmlSchemaSet xs 。 (当你这样做时,生成的XSD将反映返回的模式。)