2

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ルート オブジェクトとしてシリアル化すると、次のようになります。

<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
{
}

しかし、そうすると、Type 'PersonDTO' は IXmlSerializable ではなく、DataContractAttribute 属性を持つことができないDataContractSerializerという例外がスローされます。

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

しかし、属性を介してこれを自動的に行う方法はありますか?

4

1 に答える 1

4

これは、2 つの方法のいずれかで属性を使用して行うことができます。

まず(驚くべきことに)古い型の[XmlRoot]属性を適用すると、そこに指定された名前空間と名前がルート データ コントラクトの名前空間と名前として使用されます。XmlSerializerDataContractSerializer

[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>

ノート:

  • DataContractSerializerXmlQualifiedNameスキーマ プロバイダ メソッドによって返されたのみを使用します。ただし、 を使用して型の XSD を生成するか、 を使用して型をsvcutil.exeシリアル化するXmlSerializer予定がある場合は、もっともらしいものを に入力する必要がありますXmlSchemaSet xs。(そうすると、生成された XSD に返されたスキーマが反映されます。)
于 2017-12-18T02:00:01.263 に答える