XMLシリアル化

XMLシリアル化を用いることで、オブジェクトの状態ファイルに保存できます。一方で保存すべき対象がアプリケーションに関するものならば、それはアプリケーション設定で管理します。

シリアル化 (Serialize)

オブジェクトをストリームに変換し、ファイルへ書き込みます。

シリアル化の対象となるクラス

public class ClassName
{
    public  int publicField;   // publicなフィールド
    private int privateField;  // privateなフィールド (シリアル化の対象外)

    // get、setアクセサを有する publicなプロパティ
    public int Property
    {
        get { return privateField; }
        set { privateField = value; }
    }

    // getアクセサのみの publicなプロパティ (シリアル化の対象外)
    public int ReadOnliProperty
    {
        get { return privateField; }
    }

    // publicな既定のコンストラクタ
    public ClassName()
    {
    }
}

対象となるクラスの条件

  • publicである
    … publicでないクラスをシリアル化しようとすると「保護レベルの設定が原因で 'ProjectName.ClassName' にアクセスできません。パブリックの型のみ処理できます。」として、InvalidOperationException例外が発生します。
  • publicな既定のコンストラクタを有する
    … publicな既定のコンストラクタを持たないクラスをシリアル化しようとすると「ProjectName.ClassName にはパラメーターを持たないコンストラクターが含まれていないため、これをシリアル化することはできません。」として、InvalidOperationException例外が発生します。

対象となる要素

  • publicなフィールド
  • get、setアクセサを有する、publicなプロパティ

サポートされないオブジェクト

TimeSpan構造体はXmlSerializerによりサポートされないため、代替手段を講じる必要があります。Program It: XmlSerializer Doesn't Serialize TimeSpan to XML Duration Type (2008/09/24)

サンプルコード

class Program
{
    static void Main()
    {
        string fileName = "C:/sample.xml";

        ClassName foo = new ClassName();
        foo.publicField = 1;
        foo.Property    = 2;

        // シリアル化の対象となるクラスの型を指定して XmlSerializerを作成する
        using( System.IO.TextWriter writer = new System.IO.StreamWriter( fileName ) )
        {
            System.Xml.Serialization.XmlSerializer serializer
                = new System.Xml.Serialization.XmlSerializer( typeof( ClassName ) );

            // 指定のオブジェクトをシリアル化する
            serializer.Serialize( writer, foo );
        }
    }
}

これを実行すると、次のようなXMLファイルが作成されます

<?xml version="1.0" encoding="utf-8"?>
<ClassName xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <publicField>1</publicField>
  <Property>2</Property>
</ClassName>

作成されたXMLファイルは、逆シリアル化によりオブジェクトに復元できます。

このサンプルではXmlSerializerのコンストラクタにTypeクラスを渡していますが、これ以外にも下表で示すコンストラクタがあります。

コンストラクタ  
XmlSerializer(Type)  
XmlSerializer(Type, String)  
XmlSerializer(Type, Type[])  
XmlSerializer(Type, XmlAttributeOverrides)  
XmlSerializer(Type, XmlAttributeOverrides, Type[], XmlRootAttribute, String)  
XmlSerializer(Type, XmlAttributeOverrides, Type[], XmlRootAttribute, String, String)  
XmlSerializer(Type, XmlRootAttribute)  
XmlSerializer(XmlTypeMapping)  
コンストラクター - XmlSerializer クラス (System.Xml.Serialization) | MSDN

XmlSerializerのインスタンスの生成時、「ファイルまたはアセンブリ 'mscorlib.XmlSerializers, Version=x.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。」としてFileNotFoundException例外が発生することがあります。これはXmlSerializerのアセンブリが用意されていないのが原因であり、sgen.exeでこのファイルを作成することで解決できます。もしくはこのファイルは存在しなければ自動で作成されるため、この例外を無視しても問題はありません。c# - XmlSerializer giving FileNotFoundException at constructor - Stack Overflow

配列

シリアル化の対象が配列の場合も同様に処理できます。

ClassName[] foo = new ClassName[10];

...

XmlSerializer serializer = new XmlSerializer( typeof( ClassName[] ) );
List<ClassName> foo = new List<ClassName>()

...

XmlSerializer serializer = new XmlSerializer( typeof( List<ClassName> ) );

これらは次のように出力されます。

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfClassName xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ClassName>
    <field>1</field>
  </ClassName>
  <ClassName>
    <field>1</field>
  </ClassName>
</ArrayOfClassName>

しかしArrayListのようにObject型を返すクラスでは型を特定できないため、「ProjectName.ClassName は指定されていません。スタティックに使用できない型を指定するには XmlInclude または SoapInclude 属性を使ってください。」としてInvalidOperationException例外が発生します。この場合にはXmlSerializerのコンストラクタで、Objectに含まれる可能性のある型をすべて指定するようにします。XmlSerializer コンストラクター (Type, Type[]) (System.Xml.Serialization) | MSDN

ArrayList foo = new ArrayList();
foo.Add(new ClassName());

...

XmlSerializer serializer = new XmlSerializer(
    typeof( ArrayList ),
    new Type[] { typeof(ClassName) } );
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfAnyType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <anyType xsi:type="ClassName">
    <field>1</field>
  </anyType>
  <anyType xsi:type="ClassName">
    <field>1</field>
  </anyType>
</ArrayOfAnyType>

出力される XML形式のカスタマイズ

シリアル化の対象となるクラス自身やそのメンバに属性を付加することで、出力されるXMLの形式を変更できます。

属性 機能 適用前 適用後
XmlAttribute メンバをXMLの属性とする
<ClassName>
  <field>256</field>
</ClassName>
<ClassName field="256" />
XmlArray 配列の名前を変更する
<MemberName>
  <TypeName>
    <field>256</field>
  </TypeName>
</MemberName>
<OriginalName>
  <TypeName>
    <field>256</field>
  </TypeName>
</OriginalName>
XmlArrayItem 配列の要素名を、型名から任意の名前に変更する
<MemberName>
  <TypeName>
    <field>256</field>
  </TypeName>
</MemberName>
<MemberName>
  <OriginalName>
    <field>256</field>
  </TypeName>
</MemberName>
XMLシリアル化を制御する属性 | MSDN

逆シリアル化 (Deserialize)

ファイルをストリームから読み込み、オブジェクトに変換します。

class Program
{
    static void Main()
    {
        string fileName = "C:/sample.xml";
        ClassName foo;

        using( System.IO.TextReader reader = new System.IO.StreamReader( fileName ) )
        {
            System.Xml.Serialization.XmlSerializer serializer
                = new System.Xml.Serialization.XmlSerializer( typeof( ClassName ) );

            foo = ( ClassName )serializer.Deserialize( reader );
        }
    }
}

逆シリアル化を行うためには、環境変数TEMPにより定義される一時フォルダに書き込むための、アクセス権が必要となります。

MSDN (Microsoft Developer Network) から検索