Deserialize a Serialized Nested Type Within a JSON Object

Download full source code.

I’ve seen people struggle with this, so I thought I’d write a quick post about it.

If you are using a queue or event bus, that service will have its own message or event type. That type will have a property for the payload you want to send or receive. By payload, I mean the business-related data you want to send or receive.

Some of these queues use a string as a payload, instead of a strongly typed object. This works fine, but it means you will need to serialize and deserialize the payload yourself.

When sending a message the payload will be serialized and added to the message, then the message will be serialized and sent over the wire. This means that the payload will be serialized twice.

On the receiving end, the message needs to be deserialized, and the payload needs to be deserialized.

With generics and extension/static methods, this is pretty easy to do.

Serialized message

Here is an example of a message with a serialized payload -

  "Messages": [
      "MessageId": "461e9f7c-3e88-4f55-ad04-7e37daccf677",
      "MessageBody": "{\"Address\":\"123 Main St\",\"City\":\"Seattle\",\"State\":\"WA\",\"Owner\":{\"Name\":\"Alan Adams\",\"Age\":11}}",
      "MessageSource": "Somewhere",
      "SentTimestamp": "2023-12-12T13:04:57.1833671-05:00",
      "SenderId": "12345"

You can see that the MessageBody has its quotes escaped.

To deserialize the payload, you will first deserialize the message, and then deserialize the payload.

Deserialize the message

Once you know that you need to perform two deserializations, it’s pretty easy to do.

QueueEvent queueEvent = JsonSerializer.Deserialize<QueueEvent>(jsonString)!;
foreach (var message in queueEvent.Messages)
    var house = message.ExtractInnerObject<House>(); // this <House> is the type of the inner object but can be any type

public static class QueueEventMessageExtensions
    public static T ExtractInnerObject<T>(this Message message)
        return JsonSerializer.Deserialize<T>(message.MessageBody);

The attached source code has a working version of this where a message is created, the payload is serialized, and everything is saved to a file. Then the file is read, the message is deserialized, and the payload is deserialized.

Why do it this way?

Why serialize the payload, and then serialize the message? Why not have a generic message type, that can have any type for the payload?

I can think of a few reasons -

  • the original language used didn’t support generics or a similar feature and now it’s too late to change
  • you might want to generate a checksum for the payload and add that to the message

The better way

I’m not a fan of serializing the payload, adding it to the message, and then serializing that too. I think the better approach is to have a message that has a strongly typed payload. This way you only need to serialize the message once. In another post, I’ll show how that can be done.

Download full source code.

comments powered by Disqus