コード内のほとんどすべてのオブジェクトが拡張される、ユーザー定義のクラスがあります。このクラスに iXmlSerializeable を実装して、オブジェクトをシリアル化する方法を基本レベルで制御できるようにしたいと考えています。具体的には、オブジェクト内の DateTime 文字列を特定の方法で処理する必要があります。はい、シリアル化された DateTime から TimeZone 情報を取り除くようにアプリケーションをだますには、おそらくもっと良い方法があります。ただし、私の要件により、この顔を無視する必要があります。この効果を達成するために、アプリケーション内の他の場所のコードに影響を与えたり変更したりする必要のない、本質的に単一の関数にこれを書き込む方法が必要です。
これまでの実装は次のとおりです(リーダーのみ、まだライターを開始していません):
public void ReadXml(System.Xml.XmlReader reader)
{
//loop through properties, Read property by name, converting as necessary based on property type.
Type curType = this.GetType();
PropertyInfo[] pis;
PropertyInfo prop = null;
List<PropertyInfo> piList = new List<PropertyInfo>();
//get all fields and properties (this does not need to be recursive, properties of the underlying object DO appear at this stage, unlike fields.
pis = curType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (PropertyInfo pi in pis)
piList.Add(pi);
bool isEmpty = reader.IsEmptyElement;
reader.ReadStartElement();
if (!isEmpty)
{
bool isProp = false;
while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
{
if (!reader.IsEmptyElement)
{
if (piList.Select(a => a.Name).Contains(reader.Name))
isProp = true;
if (!isProp)//property not found?
{
reader.Skip();//cant do anything with it...keep going.
continue;
}
#region property set
prop = piList.Where(a => a.Name.Equals(reader.Name)).Single();
if (prop.CanWrite)
{
if (prop.PropertyType.BaseType.IsGenericType)
{//this should be one of the Rig Collections, are there any false positives?
ReadXmlCollection(prop, reader);
}
else if (prop.PropertyType.IsSubclassOf(typeof(BusinessObjectBase)))
{
//have to create a 'new' object, then call its reader to fill it in, then set the property value.
object newObj = Activator.CreateInstance(prop.PropertyType, false);
prop.PropertyType.GetMethod("ReadXml").Invoke(newObj, new object[] { reader });
prop.SetValue(this, newObj, null);
}
else
{
string elVal = reader.ReadString();
if (!String.IsNullOrEmpty(elVal))
{
if (prop.PropertyType == typeof(String))
prop.SetValue(this, elVal, null);
else if (prop.PropertyType == typeof(int) || prop.PropertyType == typeof(int?))
prop.SetValue(this, Convert.ToInt32(elVal), null);
else if (prop.PropertyType == typeof(double) || prop.PropertyType == typeof(double?))
prop.SetValue(this, Convert.ToDouble(elVal), null);
else if (prop.PropertyType == typeof(bool) || prop.PropertyType == typeof(bool?))
prop.SetValue(this, Convert.ToBoolean(elVal), null);
else if (prop.PropertyType == typeof(DateTime) || prop.PropertyType == typeof(DateTime?))
{//special handle
elVal = System.Text.RegularExpressions.Regex.Replace(elVal, @"(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2}\.?\d*)([\+|-]\d{2}:\d{2})", "$1 $2");
prop.SetValue(this, Convert.ToDateTime(elVal), null);
}
}
}
}
#endregion
}
if (prop == null)//read anyway, something weird has happened?
{
if (!reader.Read())
break;
}
else if(prop.Name.Equals(reader.Name))//only read if we are still on this node, otherwise, this has already been done.
if (!reader.Read())
break;
prop = null;
isProp = false;
}
}
reader.ReadEndElement();
}
private void ReadXmlCollection(PropertyInfo prop, System.Xml.XmlReader reader)
{
string colName = reader.Name;
object collectionIntance = Activator.CreateInstance(prop.PropertyType, false);
reader.ReadStartElement();
//now we are at the first element in the collection...what is it?
Type ColType = prop.PropertyType.BaseType.GetGenericArguments()[0];
while (reader.Name != colName && reader.NodeType != System.Xml.XmlNodeType.EndElement)
{
object colObj = Activator.CreateInstance(ColType, false);
ColType.GetMethod("ReadXml").Invoke(colObj, new object[] { reader });
prop.PropertyType.GetMethod("Add", new Type[] {ColType}).Invoke(collectionIntance, new object[] { colObj });
}
prop.SetValue(this, collectionIntance, null);
reader.ReadEndElement();
}
私はそれがうまくいくと思います...しかし、それも地獄のように遅いです。任意の提案をいただければ幸いです。
また、「正気ではなく、決して機能しないグッドロード」などの一般的な考えも気にしません...または同様のことです。これは抽象基本クラスに実装されており、すでに拡張された型で実行されていることに注意してください。
これは、私が読む必要があるデータのスタイルの非常に切り捨てられ、大幅に単純化された例です。
<RigX>
<isDirty>True</isDirty>
<RXJob>
<prop1>1.3</prop1>
<prop2>False<prop2>
<prop3>1984-08-10T09:30:00-06:00</prop3>
<prop4 />
<prop5>Hello World</prop5>
</RXJob>
<RXCollection>
<ColObject>
<p1>Yo</p1>
<p2>Yellow</p2>
</ColObject>
<ColObject>
<p1>No</p1>
<p2>Orange</p2>
</ColObject>
</RXCollection>
<RXNestedCollection>
<RXNestedObject>
<RXAnotherCollection>
<RXAnotherObject>
<p1>1.9</p1>
</RXAnotherObject>
</RXAnotherCollection>
<RXObject>
<prop1>nope</prop1>
<prop2>Im On A Horse!</prop2>
</RXObject>
</RXNestedObject>
</RXNestedCollection>
</RigX>
この例では、RigX は外側の単一のオブジェクトです。IsDirty は RigX のプロパティです。同様に、RXJob、RXCollection、RXNestedCollection は RigX のプロパティです。RXJob はオブジェクトで、他の 2 つはコレクションです。RXCollection は、不明な数のユーザー定義オブジェクトを含むコレクションであり、それぞれがプリミティブのみを含む単純なオブジェクトです。RXNestedCollection には不明な数のユーザー定義オブジェクトが含まれており、各オブジェクトには他のユーザー定義オブジェクトとコレクションが含まれています。