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

后端 未结 4 2047
[愿得一人]
[愿得一人] 2020-11-27 08:07

I\'m receiving a JSON object from a public API with a property that, itself, is an escaped JSON string.

{
   \"responses\":[
      {
         \"info\":\"keep         


        
相关标签:
4条回答
  • 2020-11-27 08:10

    To convert a json encoded as json string to Jobject, you can always use the technique below,

    var token = JToken.Parse(text);
    var json = JObject.Parse((string) token);
    
    0 讨论(0)
  • 2020-11-27 08:11
    1. you can deserialize it into an intermediary class that has a property: string Body {get; set;}
    2. deserialize the "body" string into it's appropriate type
    3. create a new instance of your a class that represents your destination model.
    4. serialize that model

    Here's a program that does it with the dynamic type and anonymous objects.

    static void Main(string[] args)
    {
        var json = File.ReadAllText("JsonFile1.json");
        dynamic obj = JsonConvert.DeserializeObject(json);
    
        var dest = new
        {
            responses = ((IEnumerable<dynamic>)obj.responses).Select(x => new
            {
                info = x.info,
                body = JsonConvert.DeserializeObject((string)x.body)
            })
        };
    
        var destJson = JsonConvert.SerializeObject(dest);
        File.WriteAllText("JsonFile2.json", destJson);
    }
    

    Alternatively, you can just construct a new version of whatever your destination type is instead of an anonymous type, if you don't feel like reserializing the josn.

    0 讨论(0)
  • 2020-11-27 08:26

    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 parses the incoming embedded JSON string literal into a LINQ to JSON hierarchy, then deserializes that:

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

      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 Respons
    {
        public string info { get; set; }
        public Body body { get; set; }
    }
    
    public class RootObject
    {
        public List<Respons> responses { get; set; }
    }
    
    0 讨论(0)
  • 2020-11-27 08:33

    Here's the workable solution I used based off of Sam I am's answer:

    dynamic obj = JsonConvert.DeserializeObject(json);
    foreach (var response in (IEnumerable<dynamic>)obj.responses)
    {
        response.body = JsonConvert.DeserializeObject((string)response.body);
    }
    string result = JsonConvert.SerializeObject(obj);
    
    0 讨论(0)
提交回复
热议问题