我有一个用户定义的类,我的代码中的几乎所有对象都扩展了它。我想在这个类上实现 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 是一个对象,另外两个是集合。RXCollection 是一个集合,其中包含未知数量的用户定义对象,每个对象都是仅包含原始对象的简单对象。RXNestedCollection 包含未知数量的用户定义对象,每个对象都包含其他用户定义对象和集合。