Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON.NET deserialize JSON object stored as property

I have a JSON message to deserialize with a string property containing the JSON of another object. I have the following classes

public class Envelope
{
    public string Type { get; set; }
    public Message InnerMessage { get; set; }
}

public class Message
{
    public string From { get; set; }
    public string To { get; set; }
    public string Body { get; set; }
}

the JSON message I receive is in this format:

{
    Type : "send",
    InnerMessage : "{ From: \"sender\", To: \"receiver\", Body: \"test\" }"
}

note that InnerMessage contains the serialization of the Message class, not the JSON of the class.

If I keep the type of InnerMessage property to Message, the standard JSON.NET deserialization fails.

If I change the InnerMessage to string, the serialization works but after I need to deserialize again the content of InnerMessage to Message class:

Envelope envelope = JsonConvert.DeserializeObject<Envelope>(jsonMessage);
Message innerMessage = JsonConvert.DeserializeObject<Envelope>(envelope.InnerMessage);

There is some way to keep the InnerMessage property of Envelope to Message and tell JSON.NET to treat the string value to be deserialized automatically?

like image 455
Guido Preite Avatar asked Dec 11 '25 07:12

Guido Preite


2 Answers

You need a custom JsonConverter

class StringTypeConverter : Newtonsoft.Json.JsonConverter
{
    public override bool CanRead => true;
    public override bool CanWrite => true;

    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        string json = (string)reader.Value;
        var result = JsonConvert.DeserializeObject(json, objectType);
        return result;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var json = JsonConvert.SerializeObject(value);
        serializer.Serialize(writer, json);
    }
}

and add a JsonConverterAttribute to the InnerMessage property

public class Envelope
{
    public string Type { get; set; }
    [Newtonsoft.Json.JsonConverter(typeof(StringTypeConverter))]
    public Message InnerMessage { get; set; }
}

public class Message
{
    public string From { get; set; }
    public string To { get; set; }
    public string Body { get; set; }
}

and now you can serialize/deserialize the Envelope class with

var envelope = JsonConvert.DeserializeObject<Envelope>( jsonMessage );
like image 188
Sir Rufo Avatar answered Dec 13 '25 22:12

Sir Rufo


You can do this with a custom converter. For example:

public class EnvelopeConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, 
        JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, 
        object existingValue, JsonSerializer serializer)
    {
        var envelope = JObject.Load(reader);

        var type = envelope["Type"].ToString();
        var message = JsonConvert.DeserializeObject<Message>(
            envelope["InnerMessage"].ToString());

        return new Envelope
        {
            Type = type,
            InnerMessage = message
        };
    }

    public override bool CanRead
    {
        get { return true; }
    }

    public override bool CanConvert(Type objectType)
    {
        return true;
    }
}

And use it like this:

Envelope envelope = JsonConvert.DeserializeObject<Envelope>(
    jsonMessage, new EnvelopeConverter());
like image 37
DavidG Avatar answered Dec 13 '25 22:12

DavidG