Implementing Serialization Using AOP

Jan 31, 2017
-
9
minute read

Well-designed objects do one thing, one thing well, and one thing only. They are not burdened by external concerns. They are not cluttered with things that should be the responsibility of another object. The domain objects in your application should be strictly concerned with the business problem. Unless you working for a technology solutions company, there should not be any code related to serialization, transfer protocols, user permissions, or database persistence in your application core. Keep your objects clean and tightly-designed!

In case your objects are polluted with these concerns, I will show you what I do to decouple them from serialization concerns. Examples are in C#.

Polluted Objects

It starts innocently enough with just an explicit attribute, and the requirement to expose public property setters.

For others, it might be even more invasive, involving special constructors, implementing interfaces, explicit initialization, and value output code. The boilerplate code for serialization can easily become more extensive than the human-oriented content. That should raise a giant red flag in your mind!

For a simple immutable 2-property object, 24 lines is excessive. Also notice that for every property that is added, a developer must remember to write three new additional lines of code: one in the standard constructor, one in the serialization constructor, and one in GetObjectData(). This makes the change more costly than it needs to be.

Creating Transformations

While generally in programming, it’s best to avoid using Reflection, serialization is one of the few times when using Reflection is essential. With a little bit of work, you can build simple transformation objects which plug in to your application’s external messaging. The basic strategy is, use Serialization transformations for sending messages and Deserialization transformations for receiving messages.

Here is an example transformation that enables an object to exposes its Public Properties through the ISerializable interface:

With the above transformation, it would be also be trivial to serialize your objects using all lowercase property names without requiring any JsonProperty attributes. info.AddValue(prop.Name.ToLower(), prop.GetValue(_obj));

Here is a similar way to skip the reflection part and plugin directly to a serialization library like JSON.Net:

The more complicated part is Deserialization. The best way to adapt this for your application is to setup unit tests for your particular scenarios and tinker with the process until you achieve the desired results.

Here is an example adaption that allows JSON.Net to discard the library-required cruft and return your pure objects after the Deserialization process:

publicclassNewtonsoftDeserialized<T>{privatereadonlystring_json;publicNewtonsoftDeserialized(stringjson){_json=json;}publicTCreate(){returnJsonConvert.DeserializeObject<NewtonsoftSerializable<T>>(_json).Create();}privateclassNewtonsoftSerializable<T>:ISerializable{privatereadonlySerializationInfo_info;publicNewtonsoftSerializable(SerializationInfoinfo,StreamingContextcontext){_info=info;}// Must implement ISerializable in order for Newtonsoft to usepublicvoidGetObjectData(SerializationInfoinfo,StreamingContextcontext){thrownewNotImplementedException();}publicTCreate(){varobj=newSerializationFactory<T>().Create();foreach(varentryin_info)if(HasProperty(obj,entry.Name))WritePropertyValue(obj,entry.Name,GetValue(entry.Value,GetPropertyType(obj,entry)));returnobj;}privateboolHasProperty(Tobj,stringpropertyName){returnobj.GetType().GetProperty(propertyName)!=null;}privateTypeGetPropertyType(Tobj,SerializationEntryentry){returnobj.GetType().GetProperty(entry.Name).PropertyType;}privateobjectGetValue(objectobj,TypetoType){if(!(objisJToken))thrownewInvalidOperationException($"Can only convert Newtonsoft JToken objects.");return((JToken)obj).ToObject(toType);}// Reflection magic necessary for inherited public Properties. privatevoidWritePropertyValue(objectobj,stringpropertyName,objectvalue){varcurrentType=obj.GetType();while(currentType!=null){varprop=currentType.GetProperty(propertyName,BindingFlags.Public|BindingFlags.Instance);if(prop!=null&&prop.CanWrite){prop.SetValue(obj,value);return;}currentType=currentType.BaseType;}}}}

Plugging In The New Aspect

Now, with the new transformation, plug them in at the edges of your application, when sending and receiving messages.

When I started this post, I wanted to say that implementing Serialization using AOP is simple and easy. The truth is, it’s not easy. It takes some work. There isn’t a simple general solution that solves all of the details. The more complex your messaging objects are, the tougher it is to wire in a Serialization Aspect. Simpler objects make it much easier.

However, if you have more than a few message objects in your application, using AOP instead of designing every object with additional concerns pays off dividends. When you keep your objects simpler and confine each one to it’s own subdomain, your code is easier to read, easier to understand, easier to test, and easier to reason about. This gives major benefits when you add a new feature to your application or add an existing one. You won’t have to ask yourself if there is some aspect you forgot to include in your simple message objects, or if you missed a critical test case. This ease of mind is well worth a couple of days trial-and-error to extract the messaging responsibilities from your domain.

The best argument that I can make for implementation Serialization using AOP is the practical one. The second best argument is just to show you the difference.