昨日に続き、実験。XmlSerializerを自前で実装してみます(簡易ですけど)。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
namespace Sample
{
internal class Program
{
private static void Main(string[] args)
{
MyClass mc = new MyClass();
mc.Value = "ぴょんぴょん";
Serialize(mc, @"D:\hoge.xml");
IEnumerable<Object> deserializedObjects = DeserializeObjects(@"D:\hoge.xml");
foreach (object deserializedObject in deserializedObjects)
{
if (deserializedObject is MyClass)
{
MyClass mc2 = deserializedObject as MyClass;
Debug.Assert(String.Equals(mc.Value, mc2.Value));
}
}
}
/// <summary>
/// オブジェクトをファイルにシリアライズします。
/// </summary>
/// <param name="o">シリアライズするオブジェクト</param>
/// <param name="filePath">ファイル名</param>
public static void Serialize(Object o, String filePath)
{
using (StreamWriter streamWriter = new StreamWriter(filePath, false, Encoding.UTF8))
{
using (XmlWriter xmlWriter = XmlWriter.Create(streamWriter))
{
Type type = o.GetType();
xmlWriter.WriteStartElement("class");
xmlWriter.WriteAttributeString("type", type.FullName);
ForEachPropertyInfo(type, delegate(PropertyInfo propertyInfo)
{
if (!propertyInfo.CanRead || !propertyInfo.CanWrite)
return;
Object propertyValue;
propertyValue = type.InvokeMember(propertyInfo.Name,
BindingFlags.GetProperty, null, o, null,
CultureInfo.CurrentCulture);
xmlWriter.WriteAttributeString(propertyInfo.Name, propertyValue.ToString());
});
xmlWriter.WriteEndElement();
}
}
}
/// <summary>
/// ファイルに所定のフォーマットでシリアライズされたデータを
/// デシリアライズします。
/// </summary>
/// <param name="filePath">ファイル名</param>
public static IEnumerable<Object> DeserializeObjects(String filePath)
{
using (StreamReader streamReader = new StreamReader(filePath, Encoding.UTF8, true))
{
using (XmlReader xmlReader = XmlReader.Create(streamReader))
{
while (xmlReader.Read())
{
if (!xmlReader.Name.Equals("class"))
continue;
String typeName = xmlReader.GetAttribute("type");
Type type = Type.GetType(typeName);
Object instance = Activator.CreateInstance(type);
ForEachPropertyInfo(type, delegate(PropertyInfo propertyInfo)
{
if (!propertyInfo.CanRead || !propertyInfo.CanWrite)
return;
String value = xmlReader.GetAttribute(propertyInfo.Name);
type.InvokeMember(propertyInfo.Name,
BindingFlags.SetProperty, null, instance, new Object[] {value},
CultureInfo.CurrentCulture);
});
yield return instance;
}
}
}
}
/// <summary>
/// 指定した型に含まれるプロパティを全て列挙し、Actionerに渡す。
/// </summary>
/// <param name="type">プロパティを列挙したい型</param>
/// <param name="Actioner">各プロパティを処理するPropertyInfoActioner</param>
private static void ForEachPropertyInfo(Type type, PropertyInfoActioner Actioner)
{
foreach (PropertyInfo propertyInfo in type.GetProperties())
{
Actioner(propertyInfo);
}
}
private delegate void PropertyInfoActioner(PropertyInfo propertyInfo);
#region Nested type: MyClass
/// <summary>
/// (テスト用)シリアライズするクラス
/// </summary>
private class MyClass
{
private String value;
public String Value
{
get { return value; }
set { this.value = value; }
}
}
#endregion
}
}