在使用数据协定序列化程序序列化 IXmlSerializable 对象时,如何控制根元素命名空间和名称?
How can I control the root element namespace and name when serializing an IXmlSerializable object with the data contract serializer?
我有一个实现 IXmlSerializable
which I am serializing with 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
作为我的根对象序列化它,我得到:
<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
抛出一个异常说明 Type '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" />
但是有什么方法可以通过属性自动执行此操作吗?
这可以通过以下两种方式之一使用属性来完成。
首先(令人惊讶的是)如果将旧 XmlSerializer
的 [XmlRoot]
属性应用于类型,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]
属性可用于指定一个静态方法,returns数据协定名称、命名空间和架构类型:
[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 将反映返回的模式。)
我有一个实现 IXmlSerializable
which I am serializing with 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
作为我的根对象序列化它,我得到:
<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
抛出一个异常说明 Type '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" />
但是有什么方法可以通过属性自动执行此操作吗?
这可以通过以下两种方式之一使用属性来完成。
首先(令人惊讶的是)如果将旧 XmlSerializer
的 [XmlRoot]
属性应用于类型,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]
属性可用于指定一个静态方法,returns数据协定名称、命名空间和架构类型:
[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 将反映返回的模式。)