Json Messaging with C# Part 1: Messages – Obvious Code

Json Messaging with C# Part 1: Messages

In this article I thought I’d share a useful pattern I’ve being using over the last couple of years, to build self serializing and deserializing messages. In the next post I’ll demonstrate a nice modular structure for handling these messages.

Whilst I’m using the (wonderful) Newtonsoft Json libraries, this pattern can be modified to use the native .NET Json classes.

The base classes for this form of message are as follows.

01
public class JsonMessage 
02
{
03
    public string Tag { get; set; }//Could also be an Enum 
04
 
05
    public JsonMessage(string tag)
06
    {
07
        Tag = tag;
08
    }
09
 
10
    public string Serialise()
11
    {
12
        return JsonConvert.SerializeObject(this);
13
    }
14
 
15
    //Not required, but useful for network protocols that use byte arrays
16
    public byte[] SerialiseToByteArray()
17
    {
18
        return Encoding.UTF8.GetBytes(Serialise());
19
    }
20
}
21
 
22
public abstract class JsonMessage<T> : JsonMessage where T : JsonMessage<T> 
23
{
24
    public JsonMessage(string tag) : base(tag)
25
    {
26
 
27
    }
28
 
29
    public static T From(string json)
30
    {
31
        return JsonConvert.DeserializeObject<T>(json);
32
    }
33
}

Nothing too exciting here, I guess.

We’ve got an identifier for each message. In the example above, it’s a string, however an enum would work just as well – in fact, as I will mention in the next post, an enum may work better for some situations.

We’ve got a Serialise() method to convert the object into JSON, and a static method to convert JSON back into an object.

What does stand out here, indeed I’ve had colleagues pointing this out as a bug, is the generic constraint where T : JsonMessage<T>. Yes, to define a generic constraint as itself does appear to contradict the concepts of generics – to limit a generic to a single type.

What this gives us, however, is this ability to have the public static T From(string json) method of a class inheriting from this abstract class to return its own type.

To illustrate, given the followingClasses:

01
public class MessageOne : JsonMessage<messageone>
02
{
03
    public MessageOne : base("messageOne") {  }
04
 
05
    public string Command { get ; set; }
06
    public string Guid { get; set; }
07
    public int[] Params { get; set; }
08
}
09
 
10
public class MessageTwo : JsonMessage<messagetwo>
11
{
12
    public MessageTwo : base("messageTwo") {  }
13
 
14
    public string Result { get ; set; }
15
    public string ErrorMessage { get; set; }
16
}</messagetwo></messageone>

we can do the following

01
public class Server() 
02
{
03
    public void DoSomethingOverNetwork ()
04
    {
05
        MessageOne m1 = new MessageOne 
06
        {
07
            Command = "Process",
08
            Guid = "32fa150f-bf13-4919-9731-34e3bbf75913",
09
            Params = new int[]{1,2,3,4,5,6,7,8,9}
10
        };
11
        
12
        Send(m1.Serialise());//Or Send(m1.SerialiseToByteArray());
13
    }
14
}
15
 
16
public class Client()
17
{
18
    public void Receive(string json)
19
    {
20
        //Determine type of message, I will talk about this in my next post. Here, just assume messageOne
21
        var message = MessageOne.From(json);
22
 
23
        //Leaving out processing message
24
 
25
        //Respond with succesful message
26
        MessageTwo message2 = new MessageTwo 
27
        {
28
            Result = "Success",
29
            ErrorMessage = ""
30
        };
31
 
32
        //Send response
33
        Reply(message2.Serialise());
34
    }
35
}
36

It’s a deceptively simple pattern, I admit, however the ability for the static class to deserialise to its own Type, purely from inheriting from an abstract, is surprisingly powerful.

One point to consider, however, is how the process receiving the message knows what Type to deserialise the message to. There are a few different possible ways, which I shall discuss in part 2.

Leave a Reply

Your email address will not be published. Required fields are marked *