Json Messaging with C# Part 2: Message Types – Obvious Code

Json Messaging with C# Part 2: Message Types

This post is the second part of three posts about Json Messaging.

Part 1 is here
Part 3 coming soon.

In this post I want to discuss a small problem about self deserializing messages.

If we consider that we can rebuild our object from messages, simply by using:

1
MessageOne message = MessageOne.From(json);

the question arises, how do we know what type of message our received json text is? What Class do we use to call our static From method.

Simple Messages

One, simple, answer to this issue is to only use one message type and allow it be generic enough to be used for all messages.

This is fine if your messages are relatively simple and homogeneous, however quickly breaks down in scenarios where message complexity can vary. It also, in my opinion, kind of defeats to purpose of using an Object deserialising approach to messaging.

Two Step Json Deserialising

The method I employ in most cases, is to first convert the message to JsonMessage, which, if you remember, describes the object type.

From here, we can call the correct Class static From method to get our full object

1
JsonMessage plainMessage = JsonConvert.DeserializeObject<JsonMessage>(json);
2
 
3
//fairly brute force method of handling the Tag, 
4
//but I'll describe a better pattern in the next post
5
if (plainMessage.Tag == "messageOne")
6
{
7
    return MessageOne.From(json);
8
}
9
else if ... //deserialise each message type

I am aware that two step deserialising will, theoretically, double the time a message read takes to perform – but I honestly think only becomes an issue in edge case scenarios. I have used this pattern to handle thousands of network messages a minute, and, during profiling, have never found the two step deserialisation to be a bottle neck. Frankly, the performance of the Newtonsoft libraries, or indeed any decent library should be able to handle this without causing an issue.

Another argument against two step serialisation can be that is has a greater overhead of code. Using the Message Handler pattern I will describe in my next post, this code is largely obfuscated.

Embedded Type Code
Occasionally, instead of sending a pure JSON message, I have prefixed the message with a byte or two of data that describes the message type.

This method works best when, instead of using a Tag to describe a message type, an enum is used, as fixed length message headers are far preferable to a variable sized ones, as would be required if we used string Tags.

The header size need not be too large, indeed a single byte may suffice for most occasions. Certainly plan to start with redundancy, to allow for scaling, but a single byte does provide up to 256 unique message identifiers.

In this scenario, we can set up an Enum called MessageTypes.

1
public enum MessageTypes : byte
2
{
3
    Handshake = 0,
4
    Response
5
}

Here, our JsonMessage can be modified to create a message with the type discriminator in as the first byte.

01
02
    public class JsonMessage
03
    {
04
        private byte[] _messageType;// use array to help with serialisation
05
        public MessageTypes TypeOfMessage =>(MessageTypes) _messageType[0];
06
 
07
        public JsonMessage(MessageTypes type)
08
        {
09
            _messageType = new byte[] { (byte) type };
10
 
11
        }
12
 
13
        public string Serialise()
14
        {
15
            return JsonConvert.SerializeObject(this);
16
        }
17
 
18
        public byte[] SerialiseToByteArray()
19
        {
20
            //If speed is an issue, use Buffer.BlockCopy, 
21
            //but the difference is minimal
22
            return _messageType.Concat(
23
                Encoding.UTF8.GetBytes(Serialise())).ToArray();           
24
        }
25
    }
26

It is here, however, we may hit a snag.

01
02
    public abstract class JsonMessage<T> : JsonMessage where T : JsonMessage<T>
03
    {
04
        public JsonMessage(MessageTypes messageType) : base(messageType)
05
        {
06
 
07
        }        
08
 
09
        //This will not compile
10
        public static T From(byte[] message)
11
        {
12
            MessageTypes messageType = (MessageTypes)message[0];
13
            string json = Encoding.UTF8.GetString(message.Skip(1).ToArray());
14
 
15
            //Not the most encapsulated way of doing this, 
16
            //but keeping it simple for the purposes of demonstration
17
            switch (messageType)
18
            {
19
                case MessageTypes.Handshake:
20
                    return HandshakeMessage.From(json);                        
21
                case MessageTypes.Response:
22
                    return ResponseMessage.From(json);
23
                default:
24
                    throw new InvalidOperationException($"Message Type {messageType} not handled during deserialisation");                    
25
            }
26
        }
27
 
28
        public static T From(string json) => JsonConvert.DeserializeObject<T>(json);
29
    }
30

The problem we now have is two-fold.

  1. Our T return to deserialise the byte array not longer compiles, as we are trying to return Typed messages with from a Generic return type. We could get less specific here, by returning JsonMessage instead of T, however we still have problem 2:
  2. Even if we change the method return type to JsonMessage to allow for any message type to be returned, because we are deserialising the method by calling the static T From(string json) method, we need to call this static method on the specific Message class. To deserialise a Handshake method, we need to call static From on the HandshakeMessage class (or, even, JsonMessage.From).

    Here we hit our original issue, the one that we’re trying to address here, what Class to use. Chickens and eggs, anyone?

The answer is fairly obvious. We need to separate out the identification step with the deserialisation:

JsonMessage base class

01
02
    public class JsonMessage
03
    {
04
        private byte[] _messageType;// use array to help with serialisation
05
        public MessageTypes TypeOfMessage =>(MessageTypes) _messageType[0];
06
 
07
        public JsonMessage(MessageTypes type)
08
        {
09
            _messageType = new byte[] { (byte) type };            
10
        }
11
 
12
        public string Serialise()
13
        {
14
            return JsonConvert.SerializeObject(this);
15
        }
16
 
17
        public static MessageTypes GetMessageType(byte[] byteMessage) =>
18
            (MessageTypes)byteMessage[0];
19
 
20
        public byte[] SerialiseToByteArray()
21
        {
22
            //If speed is an issue, use Buffer.BlockCopy, 
23
            //but the difference is minimal
24
            return _messageType.Concat(
25
                Encoding.UTF8.GetBytes(Serialise())).ToArray();           
26
        }
27
    }
28

Typed JsonMessage base class
01
02
    public abstract class JsonMessage<T> : JsonMessage where T : JsonMessage<T>
03
    {
04
        public JsonMessage(MessageTypes messageType) : base(messageType)
05
        {
06
 
07
        }        
08
 
09
        public static T From(byte[] message)
10
        {            
11
            string json = Encoding.UTF8.GetString(message.Skip(1).ToArray());
12
 
13
            return From(json);
14
        }
15
 
16
        public static T From(string json) => 
17
            JsonConvert.DeserializeObject<T>(json);
18
    }
19

We can now do something like the following:

01
02
    public bool HandleMessage(byte[] messageBytes)
03
    {
04
        MessageTypes type = JsonMessage.GetMessageType(messageBytes);
05
        //Again, not the most encapsulated way of doing this, 
06
        //but keeping it simple for the purposes of demonstration
07
        switch (type)
08
        {
09
            case MessageTypes.Handshake:
10
                return HandleHandshake(HandshakeMessage.From(messageBytes));
11
            case MessageTypes.Response:
12
                return HandleResponse(ResponseMessage.From(messageBytes));
13
            default:
14
                throw new InvalidOperationException($"Type {type} not handled");
15
        }
16
    }
17

In the following, final, post of this series, I will be looking a pattern I commonly use to handle messages.

Leave a Reply

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