「C Sharpの基礎 - シリアライズ」の版間の差分

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動
(ページの作成:「== 概要 == クラスの内容をバイナリファイルに保存する簡単な方法として、BinaryFormatterクラスを使う方法がある。<br> BinaryFormatte…」)
 
(文字列「</source>」を「</syntaxhighlight>」に置換)
 
(2人の利用者による、間の7版が非表示)
1行目: 1行目:
== 概要 ==
== 概要 ==
クラスの内容をバイナリファイルに保存する簡単な方法として、BinaryFormatterクラスを使う方法がある。<br>
クラスの内容をバイナリファイルに保存する簡単な方法として、<code>BinaryFormatter</code>クラスを使う方法がある。<br>
BinaryFormatterクラスを使用してクラスをシリアル化し、FileStreamでファイルに書き込む。<br>
<code>BinaryFormatter</code>クラスを使用してクラスをシリアル化して、<code>FileStream</code>でファイルに書き込む。<br>
<br>
<br>
 
<code>BinaryFormatter</code>クラスの代わりに<code>SoapFormatter</code>クラスを使って、XMLファイルに保存することもできる。<br>
'''※補足'''
<code>SoapFormatter</code>クラスの使い方は、ここで記載している方法とほぼ同様である。<br>
BinaryFormatterクラスの代わりにSoapFormatterクラスを使って、XMLファイルに保存することもできる。<br>
SoapFormatterクラスの使い方は、ここで紹介している方法とほぼ同じである。<br>
<br><br>
<br><br>


== サンプルコード ==
== バイナリファイル ==
ここでは次のようなTestClassクラスがあるものとし、BinaryFormatterクラスを使ってTestClassオブジェクトをバイナリファイルに保存する方法を記述する。
==== シリアル化 ====
  <source lang="c#">
以下に示すTestClassクラスを、<code>BinaryFormatter</code>クラスを使用してバイナリファイルに保存する。<br>
  <syntaxhighlight lang="c#">
  public class TestClass
  public class TestClass
  {
  {
35行目: 34行目:
     }
     }
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br>
まず、次のようにTestClassオブジェクトにSerializableAttribute属性を付加する必要がある。
まず、以下のようにTestClassオブジェクトの先頭に<code>SerializableAttribute</code>属性を付加する。<br>
  <source lang="c#">
  <syntaxhighlight lang="c#">
  [Serializable()]
  [Serializable()]
  public class TestClass
  public class TestClass
  {
  {
     // (省略)
     // ...省略
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br>
TestClassオブジェクトを保存、復元する具体的なコードを以下に記述する。<br>
<code>SaveToBinaryFile</code>メソッドを使用して、バイナリファイルとしてシリアル化する。<br>
SaveToBinaryFileメソッドでオブジェクトを保存し、LoadFromBinaryFileメソッドでオブジェクトの復元ができる。<br>
また、<code>LoadFromBinaryFile</code>メソッドを使用して、バイナリファイルから逆シリアル化する。<br>
  <source lang="c#">
  <syntaxhighlight lang="c#">
  using System;
  using System;
  using System.IO;
  using System.IO;
107行目: 106行目:
     }
     }
  }  
  }  
  </source>
  </syntaxhighlight>
<br>
==== シリアル化しないフィールドを指定する ====
上記のように、<code>BinaryFormatter</code>クラスを使用してシリアル化できるものは、クラスのフィールドである。<br>
この時、publicフィールド、protectedフィールド、privateフィールドも保存される。<br>
<br>
<br>
'''保存しないフィールドを指定する'''
保存しないフィールドには、<code>NonSerializedAttribute</code>属性を付加する。<br>
上記のように、BinaryFormatterを使用してオブジェクトを保存した場合、保存されるのはクラスのフィールドで、パブリックフィールドとプライベートフィールドも保存される。<br>
保存したくないフィールドにはNonSerializedAttribute属性を付ける。<br>
<br>
<br>
次の例では、TestClassの_numberフィールドをシリアル化の対象から除外し、保存されないようにしている。<br>
以下の例では、TestClassの_numberフィールドをシリアル化の対象から除外し、保存されないようにしている。<br>
  <source lang="c#">
  <syntaxhighlight lang="c#">
  [Serializable()]
  [Serializable()]
  public class TestClass
  public class TestClass
124行目: 125行目:
     // (省略)
     // (省略)
  }
  }
  </source>
  </syntaxhighlight>
<br><br>
 
== XMLファイル ==
==== シリアル化できるもの ====
* 対象となるクラスの条件
** publicである
**: publicではないクラスをシリアル化しようとすると、以下に示す<code>InvalidOperationException</code>例外が発生する。
**:<code>"保護レベルの設定が原因で 'ProjectName.ClassName' にアクセスできません。パブリックの型のみ処理できます。"</code>
** publicな既定のコンストラクタを有する
**: publicな既定のコンストラクタを持たないクラスをシリアル化しようとすると、以下に示す<code>InvalidOperationException</code>例外が発生する。
**: <code>"ProjectName.ClassName にはパラメーターを持たないコンストラクターが含まれていないため、これをシリアル化することはできません。"</code>
*: <br>
* 対象となる要素
** publicフィールド
** getアクセサおよびsetアクセサを有するpublicプロパティ
<br>
==== XMLの書式の変更 ====
シリアル化の対象となるクラスおよびクラスのメンバに属性を付加することにより、保存するXMLファイルの書式を変更することができる。<br>
<center>
{| class="wikitable" style="background-color:#fefefe;"
|+ XMLシリアル化を制御する属性
|-
! style="background-color:#66CCFF;" | 属性
! style="background-color:#66CCFF;" | 機能
! style="background-color:#66CCFF;" | 適用前
! style="background-color:#66CCFF;" | 適用後
|-
| XmlAttribute || メンバをXMLの属性とする ||
<syntaxhighlight lang="xml">
<ClassName>
  <field>256</field>
</ClassName>
</syntaxhighlight>
| <syntaxhighlight lang="c++"><ClassName field="256" /></syntaxhighlight>
|-
| XmlArray || 配列の名前を変更する ||
<syntaxhighlight lang="xml">
<MemberName>
  <TypeName>
      <field>256</field>
  </TypeName>
</MemberName>
</syntaxhighlight>
| <syntaxhighlight lang="xml">
<OriginalName>
  <TypeName>
      <field>256</field>
  </TypeName>
</OriginalName>
</syntaxhighlight>
|-
| XmlArrayItem || 配列の要素名を、型名から任意の名前に変更する。 ||
<syntaxhighlight lang="xml">
<MemberName>
  <TypeName>
      <field>256</field>
  </TypeName>
</MemberName>
</syntaxhighlight>
| <syntaxhighlight lang="xml">
<MemberName>
  <OriginalName>
      <field>256</field>
  </TypeName>
</MemberName>
</syntaxhighlight>
|}
</center>
<br>
==== シリアル化 ====
以下のクラスを、XMLファイルに保存する手順を記載する。<br>
<syntaxhighlight lang="c#">
public class TestClass
{
    private string _message;
    private int _number;
    public string Message
    {
      get {return _message;}
      set {_message = value;}
    }
    public int Number
    {
      get {return _number;}
      set {_number = value;}
    }
 
    public TestClass(string str, int num)
    {
      _message = str;
      _number  = num;
    }
}
</syntaxhighlight>
<br>
まず、次のようにTestClassオブジェクトに<code>SerializableAttribute</code>属性を付加する。<br>
<syntaxhighlight lang="c#">
[Serializable()]
public class TestClass
{
    // (省略)
}
</syntaxhighlight>
<br>
<syntaxhighlight lang="c#">
class Program
{
    static void Main()
    {
      string fileName = "$HOME/Serialize.xml";
      var myClass = new TestClass();
      myClass.publicField = 1;
      myClass.Property    = 2;
      using(var writer = new System.IO.StreamWriter(fileName))
      {
          // シリアル化の対象となるクラスの型を指定して XmlSerializerを作成する
          var serializer = new System.Xml.Serialization.XmlSerializer(typeof(TestClass));
          // 指定のオブジェクトをシリアル化する
          serializer.Serialize(writer, TestClass);
      }
    }
}
</syntaxhighlight>
<br>
上記の例を実行する時、以下のようなXMLファイルが作成される。<br>
<syntaxhighlight lang="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>
</syntaxhighlight>
<br>
上記の例では、<code>XmlSerializer</code>クラスのコンストラクタに<code>Type</code>クラスを指定しているが、これ以外にも下表に示すコンストラクタがある。<br>
<center>
{| class="wikitable" style="background-color:#fefefe;"
|-
! style="background-color:#66CCFF;" | XmlSerializerクラスのコンストラクタ
|-
| 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)
|}
[https://msdn.microsoft.com/ja-jp/library/system.xml.serialization.xmlserializer.aspx#Anchor_2 XmlSerializerクラス (System.Xml.Serialization) | MSDN]<br>
</center>
<br>
<u>※注意</u><br>
<u><code>XmlSerializer</code>クラスのインスタンスを生成する時、以下に示す<code>FileNotFoundException</code>例外が発生することがある。</u><br>
<br>
<u>これは、<code>XmlSerializer</code>クラスのアセンブリが用意されていないことが原因であり、sgen.exeファイルを実行して、このファイルを作成することで解決できる。</u><br>
<u>もしくは、このファイルが存在しない場合は自動的に作成されるため、この例外を無視しても問題ない。</u><br>
ファイルまたはアセンブリ 'mscorlib.XmlSerializers, Version=x.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。
<br>
==== 配列のシリアル化 ====
配列やListクラスの場合も、同様にシリアル化できる。<br>
<syntaxhighlight lang="c#">
// int型配列
int[] array = new int[]{1, 2, 3};
var serializer = new XmlSerializer(typeof(int[]));
// Listクラス
List<int> list = new List<int>(new int[]{1, 2, 3});
var serializer = new XmlSerializer(typeof(List<int>));
</syntaxhighlight>
<br>
これらは、以下のように出力される。<br>
<syntaxhighlight lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfInt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <int>1</int>
    <int>2</int>
    <int>3</int>
</ArrayOfInt>
</syntaxhighlight>
<br>
しかし、<code>ArrayList</code>クラスのような<code>Object</code>型を返すクラスでは型を特定できないため、以下に示す<code>InvalidOperationException</code>例外が発生する。<br>
型 ProjectName.ClassName は指定されていません。スタティックに使用できない型を指定するには XmlInclude または SoapInclude 属性を使ってください。
<br>
この場合、<code>XmlSerializer</code>クラスのコンストラクタにおいて、<code>Object</code>型に含まれる可能性のある型を全て指定する。<br>
[https://msdn.microsoft.com/ja-jp/library/e5aakyae.aspx XmlSerializerクラスのコンストラクタ (Type, Type[<nowiki>]</nowiki>) (System.Xml.Serialization) | MSDN]<br>
<syntaxhighlight lang="c#">
var obj = new ArrayList(new int[]{1, 2, 3});
var serializer = new XmlSerializer(typeof(ArrayList), new Type[]{typeof(int)});
</syntaxhighlight>
<br>
<syntaxhighlight lang="xml">
<?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="xsd:int">1</anyType>
    <anyType xsi:type="xsd:int">2</anyType>
    <anyType xsi:type="xsd:int">3</anyType>
</ArrayOfAnyType>
</syntaxhighlight>
<br>
==== 逆シリアル化 ====
作成されたXMLファイルは、逆シリアル化によりオブジェクトに復元できる。<br>
<br>
以下の例では、XMLファイルをストリームから読み込み、オブジェクトに変換している。<br>
<br>
<u>Windowsの場合、逆シリアル化を行うには、環境変数TEMPにより定義される一時フォルダに書き込むため、アクセス権が必要となる場合がある。</u><br>
<syntaxhighlight lang="c#">
class Program
{
    static void Main()
    {
      string fileName = "$HOME/Serialize.xml";
      TestClass myClass;
      using(var reader = new System.IO.StreamReader(fileName))
      {
          var serializer = new System.Xml.Serialization.XmlSerializer(typeof(TestClass));
          myClass = (TestClass)serializer.Deserialize(reader);
      }
    }
}
</syntaxhighlight>
<br><br>
<br><br>


__FORCETOC__
__FORCETOC__
[[カテゴリ:C_Sharp]]
[[カテゴリ:C_Sharp]]

2021年11月24日 (水) 17:32時点における最新版

概要

クラスの内容をバイナリファイルに保存する簡単な方法として、BinaryFormatterクラスを使う方法がある。
BinaryFormatterクラスを使用してクラスをシリアル化して、FileStreamでファイルに書き込む。

BinaryFormatterクラスの代わりにSoapFormatterクラスを使って、XMLファイルに保存することもできる。
SoapFormatterクラスの使い方は、ここで記載している方法とほぼ同様である。


バイナリファイル

シリアル化

以下に示すTestClassクラスを、BinaryFormatterクラスを使用してバイナリファイルに保存する。

 public class TestClass
 {
    private string _message;
    private int _number;
 
    public string Message
    {
       get {return _message;}
       set {_message = value;}
    }
 
    public int Number
    {
       get {return _number;}
       set {_number = value;}
    }

    public TestClass(string str, int num)
    {
       _message = str;
       _number  = num;
    }
 }


まず、以下のようにTestClassオブジェクトの先頭にSerializableAttribute属性を付加する。

 [Serializable()]
 public class TestClass
 {
    // ...省略
 }


SaveToBinaryFileメソッドを使用して、バイナリファイルとしてシリアル化する。
また、LoadFromBinaryFileメソッドを使用して、バイナリファイルから逆シリアル化する。

 using System;
 using System.IO;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization.Formatters.Binary;
 
 public class MainClass
 {
    public static void Main()
    {
       // 保存先のファイル名
       string fileName = @"C:\test.obj";
 
       // TestClassオブジェクトを作成
       TestClass obj1 = new TestClass("テスト", 123);
 
       // オブジェクトの内容をファイルに保存する
       SaveToBinaryFile(obj1, fileName);
 
       // オブジェクトの内容をファイルから読み込み復元する
       TestClass obj2 = (TestClass)LoadFromBinaryFile(fileName);
 
       // 読み込んだオブジェクトの内容を表示
       Console.WriteLine(obj2.Message);
       Console.WriteLine(obj2.Number);
 
       Console.ReadLine();
    }
 
    /// <summary>
    /// オブジェクトの内容をファイルから読み込み復元する
    /// </summary>
    /// <param name="path">読み込むファイル名</param>
    /// <returns>復元されたオブジェクト</returns>
    public static object LoadFromBinaryFile(string path)
    {
       FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
       BinaryFormatter f = new BinaryFormatter();
       // 読み込んで逆シリアル化する
       object obj = f.Deserialize(fs);
       fs.Close();
 
       return obj;
    }
 
    /// <summary>
    /// オブジェクトの内容をファイルに保存する
    /// </summary>
    /// <param name="obj">保存するオブジェクト</param>
    /// <param name="path">保存先のファイル名</param>
    public static void SaveToBinaryFile(object obj, string path)
    {
       FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);
       BinaryFormatter bf = new BinaryFormatter();
       // シリアル化して書き込む
       bf.Serialize(fs, obj);
       fs.Close();
    }
 }


シリアル化しないフィールドを指定する

上記のように、BinaryFormatterクラスを使用してシリアル化できるものは、クラスのフィールドである。
この時、publicフィールド、protectedフィールド、privateフィールドも保存される。

保存しないフィールドには、NonSerializedAttribute属性を付加する。

以下の例では、TestClassの_numberフィールドをシリアル化の対象から除外し、保存されないようにしている。

 [Serializable()]
 public class TestClass
 {
    private string _message;
 
    [NonSerialized()]
    private int _number;
    // (省略)
 }



XMLファイル

シリアル化できるもの

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

  • 対象となる要素
    • publicフィールド
    • getアクセサおよびsetアクセサを有するpublicプロパティ


XMLの書式の変更

シリアル化の対象となるクラスおよびクラスのメンバに属性を付加することにより、保存する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ファイルに保存する手順を記載する。

 public class TestClass
 {
    private string _message;
    private int _number;
 
    public string Message
    {
       get {return _message;}
       set {_message = value;}
    }
 
    public int Number
    {
       get {return _number;}
       set {_number = value;}
    }

    public TestClass(string str, int num)
    {
       _message = str;
       _number  = num;
    }
 }


まず、次のようにTestClassオブジェクトにSerializableAttribute属性を付加する。

 [Serializable()]
 public class TestClass
 {
    // (省略)
 }


 class Program
 {
    static void Main()
    {
       string fileName = "$HOME/Serialize.xml";
 
       var myClass = new TestClass();
       myClass.publicField = 1;
       myClass.Property    = 2;
 
       using(var writer = new System.IO.StreamWriter(fileName))
       {
          // シリアル化の対象となるクラスの型を指定して XmlSerializerを作成する
          var serializer = new System.Xml.Serialization.XmlSerializer(typeof(TestClass));
 
          // 指定のオブジェクトをシリアル化する
          serializer.Serialize(writer, TestClass);
       }
    }
 }


上記の例を実行する時、以下のような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>


上記の例では、XmlSerializerクラスのコンストラクタにTypeクラスを指定しているが、これ以外にも下表に示すコンストラクタがある。

XmlSerializerクラスのコンストラクタ
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クラスのインスタンスを生成する時、以下に示すFileNotFoundException例外が発生することがある。

これは、XmlSerializerクラスのアセンブリが用意されていないことが原因であり、sgen.exeファイルを実行して、このファイルを作成することで解決できる。
もしくは、このファイルが存在しない場合は自動的に作成されるため、この例外を無視しても問題ない。

ファイルまたはアセンブリ 'mscorlib.XmlSerializers, Version=x.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。


配列のシリアル化

配列やListクラスの場合も、同様にシリアル化できる。

 // int型配列
 int[] array = new int[]{1, 2, 3};
 var serializer = new XmlSerializer(typeof(int[]));
 
 // Listクラス
 List<int> list = new List<int>(new int[]{1, 2, 3});
 var serializer = new XmlSerializer(typeof(List<int>));


これらは、以下のように出力される。

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


しかし、ArrayListクラスのようなObject型を返すクラスでは型を特定できないため、以下に示すInvalidOperationException例外が発生する。

型 ProjectName.ClassName は指定されていません。スタティックに使用できない型を指定するには XmlInclude または SoapInclude 属性を使ってください。


この場合、XmlSerializerクラスのコンストラクタにおいて、Object型に含まれる可能性のある型を全て指定する。
XmlSerializerクラスのコンストラクタ (Type, Type[]) (System.Xml.Serialization) | MSDN

 var obj = new ArrayList(new int[]{1, 2, 3});
 var serializer = new XmlSerializer(typeof(ArrayList), new Type[]{typeof(int)});


 <?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="xsd:int">1</anyType>
    <anyType xsi:type="xsd:int">2</anyType>
    <anyType xsi:type="xsd:int">3</anyType>
 </ArrayOfAnyType>


逆シリアル化

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

以下の例では、XMLファイルをストリームから読み込み、オブジェクトに変換している。

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

 class Program
 {
    static void Main()
    {
       string fileName = "$HOME/Serialize.xml";
       TestClass myClass;
 
       using(var reader = new System.IO.StreamReader(fileName))
       {
          var serializer = new System.Xml.Serialization.XmlSerializer(typeof(TestClass));
 
          myClass = (TestClass)serializer.Deserialize(reader);
       }
    }
 }