Generic implementation of IXmlSerializable

Dorlovesjonas

New Member
I have a class, user defined, that almost all the objects in my code extend. I want to implement iXmlSerializeable on this class so that I can control, at a base level, the way my object is serialized. Specifically, I need the DateTime strings in the object to be treated in a specific way. Yes, there are probably better ways to trick the application into stripping the TimeZone information from a serialized DateTime. My Requirements however force me to ignore this face. I need a way to write this into essentially a single function that wont have to affect or alter the code anywhere else in the application in order to achieve this effect.Here is the implementation so far (reader Only, havent even started the writer yet):\[code\] 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(); }\[/code\]I think it works...but its also slow as hell. Any suggestions would be appreciated.I also wouldn't mind a general idea of "Good Lord that's insane and will never work"...or something similar. Keeping in mind, this is being implemented on an abstract base class, its running on already extended types.Here's an extremely truncated and massively simplified example of the style of data I need to read:\[code\]<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>\[/code\]In this example, RigX is the outer, single, object. IsDirty is a property of RigX. Likewise, RXJob, RXCollection, and RXNestedCollection are properties of RigX. RXJob is an object, and the other two are collections. RXCollection is a collection which contains an unknown number of user defined objects each of which is a simple object containing only primatives. RXNestedCollection contains an unknown number of user defined objects each of which contains other user defined objects and collections.
 
Back
Top