Using XmlSerializer to Serialize an Unserializable Object

Often when troubleshooting a production issue, the cause can be determined and recreated in a custom development environment and then fixed. However there are times when there is not enough information to determine what series of steps causes the error. This is where logging and using XmlSerializer becomes very helpful. On a recent client project, I added a bunch of logging to help me pinpoint the issue.

I already knew the error was occurring while saving an object. I thought it would be helpful to know the exact state of the object before saving rather than relying on data that could have subsequently changed in the database. So I proceeded to write code to serialize the object state into the logging table in the event of an error. Our current version of Tribridge Foundation has a dependency on CSLA, which does not completely follow the rules to make it a candidate for using XmlSerializer, so I tried other serializers including JSON and Binary. Binary is the only serializer which would work, however trying to read or even decode a binary serialized object is not fun, and very time consuming. What I really needed was a readable format like XML.

The problem was that the CSLA inherited object contained a BrokenRule class which was not compatible. Modifying the CSLA class to add the XmlIgnore tag was not an option. After doing some research, I found that overrides could be applied at runtime via the XmlAttributeOverrides class.

Successfully Using XmlSerializer

The BrokenRule class contained four properties that were read-only, which doesn’t fly with XmlSerialization. To ignore these properties, I defined the class type “BrokenRule,” the attributes I wanted applied (XmlIgnore) and added this attribute to each of the four read-only properties at runtime. I then serialized the class normally, adding in the overrides I already defined.

My fear was that this would only be the tip of the iceberg of many properties to fail XmlSerializer’s test for compatibility. Thankfully however, there was only this one issue causing serialization to fail.

Another benefit of the way Microsoft implemented the XmlAttributeOverrides class is that it doesn’t matter where in the object the class appears. You do not have to put in a fully qualified Namespace.Class.Property to define each instance. This was a boon in my case because there were many objects in a hierarchy that would have had to been tediously defined. Another benefit of the implementation is that even if the class in question (BrokenRule in this case) is not in your object graph an error is not thrown, in this case, the overrides class just does nothing. It applies the overrides if it finds it and passes it on if it doesn’t.

I deployed this new code to production and when the error occurred again, I had enough information to fix the problem.

The code below is demonstrated in this way for simplicity. The method SerializeToXml should be refactored to only contain serialization code and in an override, take an additional parameter of XmlAttributeOverrides which would be defined elsewhere.

public static string SerializeToXml(T instance) { //overrides only apply if types are included; ignored otherwise //we'll pass this to the xmlSerializer var overrides = new System.Xml.Serialization.XmlAttributeOverrides(); //define what attributes the properties should be decorated with var attrs = new System.Xml.Serialization.XmlAttributes(); attrs.XmlIgnore = true; //BrokenRule is an object inherited somewhere in a CSLA object graph //of the object we're trying to serialize. It doesn't seem to matter where //it is, this finds it and marks it. var brokenRule = typeof(Csla.Rules.BrokenRule); //decorate the properties overrides.Add(brokenRule, "RuleName", attrs); overrides.Add(brokenRule, "Description", attrs); overrides.Add(brokenRule, "Property", attrs); overrides.Add(brokenRule, "Severity", attrs); //serialize normally string serializedType = null; var x = new System.Xml.Serialization.XmlSerializer(typeof(T), overrides); using (StringWriter sw = new StringWriter()) { x.Serialize(sw, instance); serializedType = sw.ToString(); } return serializedType; } 

Have any questions, comments or concerns on using XmlSerializer? Please feel free to comment below.

Next Post