おはようございます。
ほぼすべての種類のシリアライズ可能なオブジェクトを *.INI ファイルにシリアライズする必要があります。メソッドがオブジェクトインスタンスからプロパティ情報を取得し、プリミティブ型のキー/値またはセクション + 複合型のキー/値を書き込む
を実装しようとしています。IFormatter
void Serialize(Stream, object)
シリアル化するオブジェクトの例として、このコードを取り上げます。
[Serializable]
public class EmployeeList
{
#region Properties
public List<Employee> Employees { get; private set; }
public string Banana { get; set; }
#endregion
#region Constructors
public EmployeeList()
{
Employees = new List<Employee>();
}
#endregion
#region Public Methods
public void Serialize()
{
IFormatter formatter = new IniFormatter();
using (FileStream outputStream = File.Create("employees.ini"))
{
try
{
formatter.Serialize(outputStream, this);
}
catch (SerializationException ex)
{
//...
}
}
}
#endregion
}
[Serializable]
public class Employee
{
#region Properties
public string Name
{
get;
set;
}
public Gender Gender
{
get;
set;
}
public List<Role> Roles
{
get;
set;
}
#endregion
#region Constructors
public Employee()
{
}
#endregion
}
[Serializable]
public enum Gender
{
Male,
Female,
Other
}
[Serializable]
public class Role
{
public string Name
{
get;
set;
}
public int Value
{
get;
set;
}
#region Constructors
public Role()
{
}
#endregion
}
さて、リフレクションによってプロパティの名前と値を取得しようとしたので、すべてが些細なことのように思えました。System.Reflection.TargetParameterCountException
反映しようとすると、継続的に取得されますList<Employee>
(メソッドpi.GetValue(obj, null);
を参照してください。どこにpi
あるのかPropertyInfo
)。
これは私のコードですIniFormatter
:
public class IniFormatter : IFormatter
{
#region Properties
public ISurrogateSelector SurrogateSelector { get; set; }
public SerializationBinder Binder { get; set; }
public StreamingContext Context { get; set; }
#endregion
#region Constructors
public IniFormatter()
{
Context = new StreamingContext(StreamingContextStates.All);
}
#endregion
#region IFormatter Members
public object Deserialize(Stream serializationStream)
{
throw new NotImplementedException();
}
public void Serialize(Stream serializationStream, object graph)
{
var propertyInfos = graph.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
StreamWriter sw = new StreamWriter(serializationStream);
foreach (var propertyInfo in propertyInfos)
{
if (!propertyInfo.CanRead)
{
continue;
}
if (Attribute.IsDefined(propertyInfo, typeof (NonSerializedAttribute)))
{
continue;
}
if (propertyInfo.PropertyType.IsPrimitive)
{
sw.WriteLine("{0}={1}", propertyInfo.Name, propertyInfo.GetValue(graph, null));
}
//object/complex types need to recursively call this method until the end of the tree is reached
else
{
var complexType = GetPropertyValue(graph, propertyInfo.Name);
Serialize(serializationStream, complexType);
}
}
sw.Close();
}
#endregion
public static object GetPropertyValue(object sourceObject, string propertyName)
{
if (sourceObject == null)
{
return null;
}
object obj = sourceObject;
// Split property name to parts (propertyName could be hierarchical, like obj.subobj.subobj.property
string[] propertyNameParts = propertyName.Split('.');
foreach (string propertyNamePart in propertyNameParts)
{
if (obj == null)
{
return null;
}
// propertyNamePart could contain reference to specific
// element (by index) inside a collection
if (!propertyNamePart.Contains("["))
{
PropertyInfo pi = obj.GetType().GetProperty(propertyNamePart, BindingFlags.Public | BindingFlags.Instance);
if (pi == null)
{
return null;
}
obj = pi.GetValue(obj, null);
}
else
{
// propertyNamePart is a reference to specific element
// (by index) inside a collection
// like AggregatedCollection[123]
// get collection name and element index
int indexStart = propertyNamePart.IndexOf("[") + 1;
string collectionPropertyName = propertyNamePart.Substring(0, indexStart - 1);
int collectionElementIndex = Int32.Parse(propertyNamePart.Substring(indexStart, propertyNamePart.Length - indexStart - 1));
// get collection object
PropertyInfo pi = obj.GetType().GetProperty(collectionPropertyName, BindingFlags.Public | BindingFlags.Instance);
if (pi == null)
{
return null;
}
object unknownCollection = pi.GetValue(obj, null);
// try to process the collection as array
if (unknownCollection.GetType().IsArray)
{
object[] collectionAsArray = unknownCollection as Array[];
obj = collectionAsArray[collectionElementIndex];
}
else
{
// try to process the collection as IList
IList collectionAsList = unknownCollection as IList;
if (collectionAsList != null)
{
obj = collectionAsList[collectionElementIndex];
}
else
{
// ??? Unsupported collection type
}
}
}
}
return obj;
}
}
コンソール アプリからテストを実行して、次のようにします。
Employee employee = new Employee {Name = "Nando", Gender = Gender.Male, Roles = new List<Role> {new Role {Name = "CEO", Value = 1}}};
EmployeeList employeeList = new EmployeeList();
employeeList.Banana = "It's delicious!";
employeeList.Employees.Add(employee);
employeeList.Serialize();
プロパティの値を取得できBanana
ます (プロパティ名で申し訳ありません。腹を立てると、命名規則がおかしくなってしまいます)。それ以外は何もありません。
これはかなり「問題を解決する」という質問ですが、オブジェクトを完全に反映してそのプロパティの名前と値を処理できることを理解するためのソースが見つかりません。
そこで、熟練した開発者に助けを求めて、いくつかの情報源を教えてもらいました。:-) どうもありがとう、
ナンド