本記事は、[C#での設定ファイル管理とパラメータのバージョンアップ]の続きとして、XMLのSerializeとDeSerializeによる設定情報やパラメータの扱いについて記載します。XMLのSerializeとDeserializeは、設定ファイル管理において扱いやすい特徴があり多く使わています。
Contents
Ⅰ.XML Serializeを使う方法
説明で使うXMLファイル
1 2 3 4 5 6 7 8 9 |
<ParamUserInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <ID>1002</ID> <Name>Suzuki</Name> <Grades> <Kokugo>37</Kokugo> <Suugaku>52</Suugaku> <Eigo>68</Eigo> </Grades> </ParamUserInfo> |
説明で使うデータクラス
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class ParamUserInfo { public int ID { get; set; } public string Name { get; set; } public ParamUserGrade Grades { get; set; } = new ParamUserGrade(); } public class ParamUserGrade { public int Kokugo { get; set; } public int Suugaku { get; set; } public int Eigo { get; set; } } |
XML Serializeによる設定ファイルへ書き込み
方法1と方法2の大きな違いがなく、Steamの扱いの違いである。settingによって、書き込む条件(改行有無など)を設定するためには方法1のように設定することもできる。
方法1.ファイル作成時、FileStreamとXmlWriterで書き込む方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//using System.Xml.Serialization; public static void Serialize<T>(T data, string xmlfile) { var serializer = new XmlSerializer(typeof(T)); var settings = new XmlWriterSettings(); settings.Indent = true; settings.NewLineChars = Environment.NewLine; settings.OmitXmlDeclaration = true; settings.NewLineOnAttributes = true; settings.Encoding = Encoding.UTF8; using (FileStream fs = new FileStream(xmlfile, FileMode.Create)) using (XmlWriter writer =XmlWriter.Create(fs, settings)) { serializer.Serialize(writer, data); } } |
方法2.ファイル作成時、FileStreamで書き込む方法
1 2 3 4 5 6 7 8 9 |
public static void Serialize2<T>(T data, string xmlfile) { var serializer = new XmlSerializer(typeof(T)); using (Stream writer2 = new FileStream(xmlfile, FileMode.Create)) { serializer.Serialize(writer2, data); } } |
方法2だと、XML先頭に以下のヘッダが追加される。
1 |
<?xml version="1.0"?> |
XML DeSerializeによる設定ファイルからの読み込み
DeserializeもSerializeと同じく、方法1と方法2の違いは大きくなく、Streamの扱いが違うだけである。settingによって、読み込む条件を設定するためには方法1のように設定することもできる。
方法1.ファイル作成時、FileStreamとXmlWriterで書き込む方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public static T Deserialize<T>(string xmlfile) { var serializer = new XmlSerializer(typeof(T)); var settings = new XmlReaderSettings { CheckCharacters = true }; using (FileStream fs = new FileStream(xmlfile, FileMode.Open)) using (XmlReader reader = XmlReader.Create(fs, settings)) { return (T)serializer.Deserialize(reader); } } |
方法2.ファイル作成時、FileStreamで書き込む方法
1 2 3 4 5 6 7 8 9 |
public static T Deserialize2<T>(string xmlfile) { var serializer = new XmlSerializer(typeof(T)); using (Stream fs = new FileStream(xmlfile, FileMode.Open)) { return (T)serializer.Deserialize(fs); } } |
XML SerializeとDeSerializeを呼び出す側
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
static void Main(string[] args) { var xmlfile = @"c:\temp\xmlserialize.xml"; // ParamData_Dummy.MakeDummy(xmlfile); //Deserializeによって読み込む:アプリケーション起動時 var user = UtilitySerialize.Deserialize<ParamUserInfo>( xmlfile); //アプリケーション中で値変更してみる user.ID = 2002; //Serializeによってファイルに書き込む:アプリケーション終了時 UtilitySerialize.Serialize<ParamUserInfo>(user, xmlfile); } |
XML Serializeの書き込みに影響を与える属性
XML Serializeは、クラスのまま書き込みこともできますが、XML書き込み時にクラスやメンバーの構造・名称を変更せず、プロパティなどに属性を設定することで、書き込みを調整することができます。体表的な例を示します。
属性 | 内容 |
XmlRoot | XMLのRootタグ名を指定します。 |
XmlElement | XMLのタグ名(クラスのメンバー)を指定します。 |
XmlAttribute | XMLのタグの属性(attribute)を指定します。 |
XmlIgnore | XMLには出力しないように指定します。 |
属性を使ったソースコードの例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[XmlRoot(ElementName = "root")] public class ParamUserInfo { [XmlElement(ElementName = "uid")] public int ID { get; set; } [XmlIgnore] public string Name { get; set; } public ParamUserGrade Grades { get; set; } = new ParamUserGrade(); } public class ParamUserGrade { [XmlAttribute(AttributeName = "g1")] public int Kokugo { get; set; } [XmlAttribute(AttributeName = "g2")] public int Suugaku { get; set; } [DataMember] public int Eigo { get; set; } } |
上記の結果は、以下のようになります。
1 2 3 4 5 6 7 8 |
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <uid>1002</uid> <Grades g1="36" g2="56"> <Eigo>77</Eigo> </Grades> </root> |
以下にソースコードとXML出力結果の関係を示します。XMLはsrc.1.1 xmlと比較してみるともっとわかりやすくなります。
Ⅱ.XML DataContractSerializerを使う方法
XML DataContractSerializerによる設定ファイルへ書き込み
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//using System.Runtime.Serialization; public static void SerializeDataContract<T>(T data, string xmlfile) { //普通のSerializeとは、この部分が違うだけ DataContractSerializer serializer = new DataContractSerializer(typeof(T)); var settings = new XmlWriterSettings(); using (FileStream fs = new FileStream(xmlfile, FileMode.Create)) using (XmlWriter writer = XmlWriter.Create(fs, settings)) { serializer.WriteObject(writer, data); } } |
XML DataContractSerializerによる設定ファイルからの読み込み
1 2 3 4 5 6 7 8 9 10 11 |
public static T DeserializeDataContract<T>(string xmlfile) { //Serializeとはこの部分が大きく違う DataContractSerializer serializer = new DataContractSerializer(typeof(T)); using (FileStream fs = new FileStream(xmlfile, FileMode.Open)) using (XmlReader reader = XmlReader.Create(fs, settings)) { return (T)serializer.ReadObject(reader); } } |
XML DataContractSerializerの書き込みに影響を与える属性
XML DataContractSerializerも、XML書き込み時にクラスやメンバーの構造・名称を変更せず、プロパティなどに属性を設定することで、書き込みを調整することができます。体表的な例を示します。
属性 | 内容 |
DataContract | XMLのRootタグ名を指定します。 Name:名称を変更します Namespace:NameSpaceを変更、または、””指定で削除します |
DataMember | XMLのタグ(クラスのメンバー)を指定します。 Name:出力名称を変更します Order:出力順番を変更します |
IgnoreDataMember | XMLに出力しない(除外)メンバを指定します |
属性を使ったソースコードの例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
[DataContract(Name = "User", Namespace = "")] public class ParamUserInfo { [DataMember] public int ID { get; set; } [DataMember(Name = "FullName")] public string Name { get; set; } [DataMember(Name = "Gr")] public ParamUserGrade Grades { get; set; } = new ParamUserGrade(); [IgnoreDataMember] public string Address { get; set; }; } [DataContract] public class ParamUserGrade { [DataMember(Order = 1)] public int Kokugo { get; set; } [DataMember(Order = 0)] public int Suugaku { get; set; } [DataMember(Order = 2)] public int Eigo { get; set; } } |
上記の結果は、以下のようになります。
1 2 3 4 5 6 7 8 9 |
<User xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <FullName>Suzuki</FullName> <Gr xmlns:d2p1="http://schemas.datacontract.org/2004/07/Param_SerializeXmlEx"> <d2p1:Suugaku>79</d2p1:Suugaku> <d2p1:Kokugo>59</d2p1:Kokugo> <d2p1:Eigo>95</d2p1:Eigo> </Gr> <ID>2002</ID> </User> |
交換性に影響しないバージョン管理
以下のsrc.4.1.1 Version1からバージョンアップによって、クラスメンバー名を変更する必要がある場合は、src.4.1.2 Version2のようにDataMemberAttributeを使って、交換性を維持します。
1 2 3 4 5 6 |
[DataContract] public class User { [DataMember] private string Name; } |
1 2 3 4 5 6 |
[DataContract] public class Person { [DataMember(Name = "Name")] private string FullName; } |
XmlのSerializerとDataContractSerializerの使い分け
XmlのXmlSerializerとDataContractSerializerがどんな特徴があり、どのように使うべきかについて簡単にまとめます。アプリケーションの特徴(製品の特徴)によりますので、これっとは言えないので、上記の特徴を考えた上で、どちらを使うか決めると良いです。
区分 | XmlSerializer | DataContractSerializer |
処理速度 | △ | 〇 (XmlSerializerより数十%~3倍ぐらい早いらしい) |
復元のための注意点 | デフォルトコンストラクタを経由する | デフォルトコンストラクタを経由しない |
データの順番 | 〇 データ順番変更可 | △ データ順番変更は不可 なので、VersionUP時は注意必要 |
Dictionary扱い | 〇 そのままではできない 他の型に変換して保存する |
◎ そのままで可能 |
※デフォルトコンストラクタとは、引数を持たないコンストラクタのことです。
設定ファイルのバージョンアップ
おそらく、単なるクラスのメンバー追加レベルなら大きな問題はないです(DataContractは順番に注意が必要)。アプリケーションの状況によって、どうしてもバージョンアップが必要になる場合はあります。その時は、バージョンにあったクラスを定義する必要が出てしまいます。XmlSerializerとDataContractSerializerは、かなり楽ではありますが、クラスメンバーの変化が激しい場合(例えば、メンバーの意味が変わってしまったり、メンバーの型を変えないといけない場合)などは、その都度クラスをVersion毎に生成することになるので、面戸な作業になりますので、注意が必要です。なので、もし、XML Serializeを検討しているのであれば、次に紹介するJsonによるSerializeのことも検討案のひとつとして入れておくと良いです。
バージョンアップの基本的な流れについては、「設定とパラメータファイルのバージョンアップ仕組み」を参照してください。
まとめ
XMLのSerialize(XmlSerializerとDataContractSerializer)は、強力な設定ファイルの保存方法です。ただし、バージョンアップによる設定ファイルの内容が激しい場合は、検討が必要になります。
・参考サイト
もっとSerializeについて知りたい方は、以下のサイトを参考にしてください。http://neue.cc/2011/12/10_357.html