How do I convert an escaped JSON string within a JSON object?

[*]

Your JSON contains a literal string for the "body" objects that is actually embedded, double-serialized JSON. To deserialize this to a POCO hierarchy without needing to introduce an intermediate string Json surrogate property inside any of your types, you have a few options:

  1. You could preprocess your JSON using LINQ to JSON and replace the literal "body" strings with their parsed equivalents:

        var rootToken = JToken.Parse(json);
        foreach (var token in rootToken.SelectTokens("responses[*].body").ToList().Where(t => t.Type == JTokenType.String))
        {
            token.Replace(JToken.Parse((string)token));
        }
    
        var root = rootToken.ToObject<RootObject>();
    
  2. You could introduce a generic JsonConverter for the POCO corresponding to each Body object that deserializes the incoming embedded JSON string literal as a string, then deserializes that string to the final POCO:

    public class EmbeddedLiteralConverter<T> : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(T).IsAssignableFrom(objectType);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
                return null;
            var contract = serializer.ContractResolver.ResolveContract(objectType);
            if (contract is JsonPrimitiveContract)
                throw new JsonSerializationException("Invalid type: " + objectType);
            if (existingValue == null)
                existingValue = contract.DefaultCreator();
            if (reader.TokenType == JsonToken.String)
            {
                var json = (string)JToken.Load(reader);
                using (var subReader = new JsonTextReader(new StringReader(json)))
                {
                    // By populating a pre-allocated instance we avoid an infinite recursion in EmbeddedLiteralConverter<T>.ReadJson()
                    // Re-use the existing serializer to preserve settings.
                    serializer.Populate(subReader, existingValue);
                }
            }
            else
            {
                serializer.Populate(reader, existingValue);
            }
            return existingValue;
        }
    
        public override bool CanWrite { get { return false; } }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    Then use it like:

    var root = JsonConvert.DeserializeObject<RootObject>(json, new EmbeddedLiteralConverter<Body>());
    

    Or apply it to the property corresponding to "body" like so:

    public class Response
    {
        public string info { get; set; }
        [JsonConverter(typeof(EmbeddedLiteralConverter<Body>))]
        public Body body { get; set; }
    }
    

    Note that the converter checks to see whether the incoming JSON token is a string, and if not, deserializes directly. Thus the converter should be usable when the "body" JSON is and is not double-serialized.

For testing purposes I generated the following target classes using http://json2csharp.com/:

public class Error
{
    public string message { get; set; }
    public string type { get; set; }
    public int code { get; set; }
}

public class Body
{
    public Error error { get; set; }
}

public class Response
{
    public string info { get; set; }
    public Body body { get; set; }
}

public class RootObject
{
    public List<Response> responses { get; set; }
}

Leave a Comment