Guido Preite
Guido Preite

Reputation: 15128

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?

Upvotes: 3

Views: 9128

Answers (2)

Sir Rufo
Sir Rufo

Reputation: 19096

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 );

Upvotes: 5

DavidG
DavidG

Reputation: 118937

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());

Upvotes: 3

Related Questions