Monthly Archives: November 2012

Serializing and deserializing inherited types with Json, anything you want

In my previouos post I’ve created a very simple, home made class to serialize / deserialize an object without needing to know it’s real type, so you can take really advantage of polymorphism.

But if you want a much powerfull solution that also enables you to deserialize lists and complex object graphs, I strongly recommend you the excellent NewtonSoft Json.

Get it with NuGet: Install-Package Newtonsoft.Json

If you really think about it why don’t serialize in JSON? It’s a really simple, powerful, light format and has the advantage that you can also explose it and read it from Javascript if you want without any conversion. Here are a few good reasons to use Json over Xml

So here’s the code, using the library.

Test Model

Here’s the test Model. I’ts pretty lame, I know…


public class Resource
{
    public string ResourceProperty { get; set; }
}

public class Video : Resource
{
   public string VideoProperty { get; set; }
}

First sample. Base class

Just serialize and deserialize an object, no big deal

// Deserialize and deserialize, no big deal
[TestMethod]
public void BaseClassTest()
{
   var resource = new Resource() { ResourceProperty = "Hola" };

   var resourceJSon = JsonConvert.SerializeObject(resource);
   var deserializedJson = JsonConvert.DeserializeObject(resourceJSon);

   Assert.AreEqual(deserializedJson.ResourceProperty, resource.ResourceProperty);
}

Second sample. Inherited class

Here’s the cool stuff:

// Here is the cool stuff. Serialize a derived class, and deserialize as the base class
// without loosing information
 [TestMethod]
 public void InheritedClassTest()
 {
    var video = new Video() { ResourceProperty = "Hola", VideoProperty="Video" };

    // Here is the trick!!
    // We tell the serializer to save the real type for each class
    var settings = new JsonSerializerSettings()
    {
       TypeNameHandling = TypeNameHandling.Objects
    };
    var resourceJSon = JsonConvert.SerializeObject(video, settings);

    // We must deserialize with the same settings
    var deserializedJson = JsonConvert.DeserializeObject<Resource>(resourceJSon, settings);

    Assert.AreEqual(deserializedJson.ResourceProperty, video.ResourceProperty);
    // We can cast to video with no problem
    var castedVideo = deserializedJson as Video;
    Assert.AreEqual(castedVideo.VideoProperty, video.VideoProperty);
    // sorry no polymorphism in this sample :P
 }

Internally what the serializer actually does when using the TypeNameHandling.Objects is to save the type of the object you are serializing,so it can “infer” the type when deserialazing. Just as I did in my previous article. (I swear that I didn’t copy this!!) :P

Third sample. List with base and derived class

And here’s the really cool stuff. You can also serialize a list and deserialize it without needing to know the real type of each element.

 // And this is really cool stuff
 // You can serialize for example a list
 [TestMethod]
 public void List_Test()
 {
    var resource = new Resource() { ResourceProperty = "Resource" };
    var video = new Video() { ResourceProperty = "Video", VideoProperty = "VideoMP4" };

   var list = new List<Resource>() { resource, video };

   // Again the important part are the settings
   var settings = new JsonSerializerSettings()
   {
      TypeNameHandling = TypeNameHandling.Objects
   };

   var serializedList = JsonConvert.SerializeObject(list, settings);

   var deserializedList = JsonConvert.DeserializeObject<List<Resource>> (serializedList, settings);

   // And we recover the information with NO data loss
   Assert.AreEqual("Resource", deserializedList[0].ResourceProperty);
   Assert.AreEqual("VideoMP4", ((Video)deserializedList[1]).VideoProperty);
}
Advertisements

Serializing and Deserializing inherited types in xml, simple objects

Hi there, I’m back. I’ll try to post much often now :)

The problem

Deserializing a derived class in Xml forces you to know the real type. This can be a big problem if you are trying to use polymorphism to solve a problem. If you need to know the concrete type then the value of polymorphism is completly lost.

Consider this example, you have the Resource class with an Update method. In the case of a video, it will create transcodings for múltiple browsers. In other cases, for example an image, it will optimize it for the web, and perhaps create a thumbnail. A simple class diagram might look like this:

The solution

Theory (really short)

The deserializer needs to now the real type in order to deserialize it. I propose to save this information or “discriminator” in the serialization in order to use it later.

Client code

Let me show you first the client code, It’s superb easy. The class name is InheritanceSerializer


 var originalVideo = new Video() { Name = "Sample", PendingTranscoding = true;
// First we seralize the video
 var xml = InheritanceSerializer.Serialize(originalVideo);

// then we deserialize it, no problem. It WILL have the video information
var deserializedVideo = InheritanceSerializer.Deserialize(xml) as Resource;

// Then we can perform any operation
deserializedVideo.Update();

// we COULD cast it if we want!!!
var deserializedVideo2 = deserializedVideo as Video

Solution code (finally!)

So here it is, actually really simple


    ///
    /// Allows to serialize / deserialize  objetcs and inheritance hierarchies
    /// WITHOUT knowing the type to serialize
    ///
    /// When serializing adds a "discriminator" based on the real type
    ///
    /// When deserializing "infers" the real type based on the discriminator saved during the serialization
    ///
    public class InheritanceSerializer
    {
        private const string DISCRIMINATOR = "discriminator";

        public static string Serialize(object info)
        {
            var serializer = new XmlSerializer(info.GetType());
            using (var stream = new MemoryStream())
            {
                // Serialize
                serializer.Serialize(stream, info);
                stream.Position = 0;

                // Open serialization output
                var xmlDocument = new XmlDocument();
                xmlDocument.Load(stream);
                // Add a "discriminador" based on the real type, to use it during deserialization
                var node = xmlDocument.CreateAttribute("", DISCRIMINATOR, "");
                node.InnerText = GetTypeFullName_With_AssemblyName_WihoutVersion(info.GetType());
                xmlDocument.DocumentElement.Attributes.Append(node);

                // return the xml with the discriminator
                return xmlDocument.OuterXml;
            }
        }

        public static object Deserialize(string xml)
        {
            var xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(xml);

            // read "discriminator"
            var discriminator = xmlDocument.DocumentElement.Attributes[DISCRIMINATOR];
            var typeName = discriminator.InnerText;

            // now we know the real type based on the discriminator to deserialize
            var serializer = new XmlSerializer(Type.GetType(typeName));

            using (var stream = new MemoryStream(Encoding.ASCII.GetBytes(xml)))
            {
                return serializer.Deserialize(stream);
            }
        }

        public static string GetTypeFullName_With_AssemblyName_WihoutVersion(Type type)
        {
            return string.Format("{0},{1}", type.FullName, type.Assembly.GetName().Name);
        }
    }

When to use

This solution it’s quite simple and extremelly easy to use. You can use it for simple cases. Just copy & paste the code above and that’s it. If you want the discriminator to be serialized as a node you can edit the code or check out my Github repository. In the repository I have applied TDD and some patterns like Strategy and Factory to avoid duplication and keep the code simple and elegant, but is it’s just for practicing bit.

But again I warn you, use this solution for simple cases. It won’t work if you have nested objects with inheritance nor if you have a collection