Especially when you are dealing with rest services or an MVC application, serialization of managed types to JSON objects becomes crucial. In a recent web project I was dealing, there was a need to actually serialize an enumerator. Simple, define a DataContract, assign EnumMembers and that’s all.
Let’s say my DTO model was something like:
[DataContract]
public enum MyEnumerator
{
[EnumMember(Value = “EnumFirst”)]
EnumFirst,
[EnumMember(Value = “EnumSecond”)]
EnumSecond,
[EnumMember(Value = “EnumThird”)]
EnumThird
}
[DataContract]
public class MyClass
{
[DataMember]
public MyEnumerator MyEnumValue { get; set; }
}
And the serialization went something like:
var newClassInstace = new MyClass {MyEnumValue = MyEnumerator.EnumSecond};
var jsonSerializer = new DataContractJsonSerializer(typeof (MyClass));
using (var stream = new MemoryStream())
{
jsonSerializer.WriteObject(stream, newClassInstace);
// OUTPUT: Serialized w/o Surrogate: {“MyEnumValue”:1}
System.Diagnostics.Debug.WriteLine(String.Format(“Serialized w/o Surrogate: {0}”,
Encoding.UTF8.GetString(stream.ToArray())));
stream.Position = 0;
// The deserialized object
object newMyClass = jsonSerializer.ReadObject(stream);
}
However, this issue got a bit more complicated when I actually had to serialize the enumerator names, rather than the values. Moreover, I couldn’t change the data contract since the same DTO model was used by several other clients.
First thought was to write a surrogate and replace the enum value with a string while serializing. This approach however, only worked when serializing and threw a parse exception since the primitive values (NET Framework primitive types) cannot be surrogated while deserializing.
Well, finally, why not create an actual surrogate class that is going to be used only while serializing and De-serializing. An object that carries both the value and the name of the enum member.
[DataContract(Name=“Enum”, Namespace = “CompuSight.WCF”)]
internal class EnumSurrogate
{
[DataMember(Name=“Name”)]
public string Name { get; set; }
[DataMember(Name=“Value”)]
public int Value { get; set; }
}
And the complete EnumeratorContractSurrogate looked something like this:
public class EnumeratorContractSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
return type == typeof(Enum) ? typeof(EnumSurrogate) : type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj is EnumSurrogate)
{
return ((EnumSurrogate)obj).Value;
}
return obj;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj is Enum)
{
var pair = new EnumSurrogate {Name = Enum.GetName(obj.GetType(), obj), Value = (int) obj};
return pair;
}
return obj;
}
// Rest of the methods just throws NotSupportedException / NotImplementedException
…
}
Oh and the serialization and deserialization looks something like:
var knowTypes = new List<Type> {typeof (EnumeratorContractSurrogate.EnumSurrogate)};
var jsonSerializerWithSurrogate = new DataContractJsonSerializer(typeof(MyClass), knowTypes, int.MaxValue, false, new EnumeratorContractSurrogate(), false);
using (var stream = new MemoryStream())
{
jsonSerializerWithSurrogate.WriteObject(stream, newClassInstace);
// OUTPUT: Serialized w/ Surrogate: {“MyEnumValue”:{“__type”:”Enum:CompuSight.WCF”,”Name”:”EnumSecond”,”Value”:1}}
System.Diagnostics.Debug.WriteLine(String.Format(“Serialized w/ Surrogate: {0}”,
Encoding.UTF8.GetString(stream.ToArray())));
stream.Position = 0;
// The deserialized object
object newMyClass = jsonSerializerWithSurrogate.ReadObject(stream);
}
This approach can be improved by adding some more reflection and accepting only name or only value while deserializing.