3

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?

3
  • How are you serializing your object? When I use JsonConvert.SerializeObject(envelope) I get different Json output than you. Commented Aug 11, 2017 at 16:39
  • I get the JSON from a webservice, I just need to deserialize it to the classes I created Commented Aug 11, 2017 at 16:48
  • Gotcha, looks like @SirRufo has the answer your looking for Commented Aug 11, 2017 at 16:49

2 Answers 2

5

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 );
Sign up to request clarification or add additional context in comments.

1 Comment

thanks, your solution looks cleaner than the other one, tomorrow I will test it
3

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

1 Comment

thanks for your answer, but personally I prefer the one of Sir Rufo as I can apply to the specific property and not to the class

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.