2016-08-25 2 views
0

Я получаю объект JSON от публичного API с свойством, которое само по себе является экранированной строкой JSON.Как преобразовать escape-строку JSON в объект JSON?

{ 
    "responses":[ 
     { 
     "info":"keep \"this\" in a string", 
     "body":"{\"error\":{\"message\":\"Invalid command\",\"type\":\"Exception\",\"code\":123}}" 
     }, 
     { 
     "info":"more \"data\" to keep in a string", 
     "body":"{\"error\":{\"message\":\"Other error\",\"type\":\"Exception\",\"code\":321}}" 
     } 
    ] 
} 

Как преобразовать это свойство в реальный объект JSON (неэкранированных), чтобы десериализации весь ответ, используя NewtonSoft Json.NET?

+0

Вы хотите преобразовать тело в JSON? Его уже в JSON –

+0

Я пробовал десериализацию с использованием Newtonsoft Json.NET, но я получаю ошибки, когда он пытается преобразовать тело в класс.'' Ошибка преобразования значения [JSON String], чтобы ввести '[MyClass]'. Ответы пути [0] .body ', строка 1, позиция 641. "' – Kcoder

+0

Проверьте мои изменения. Я исправил его –

ответ

0

Вот осуществимое решение я использовал основанный прочь 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
  1. вы можете десериализации его в качестве посредника класса, который имеет свойство: string Body {get; set;}
  2. десериализации «тело» строки в это соответствующий тип
  3. создать новый экземпляр вашего класса, который представляет модель назначения.
  4. сериализации этой модели

Вот программа, которая делает это с динамическим типом и анонимных объектов.

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

В качестве альтернативы, вы можете просто построить новую версию независимо от вашего типа назначения вместо анонимного типа, если вы не чувствуете, как reserializing в josn.

+0

@downvoter Является ли мой ответ неправильным? Кто-то вроде OP, который уже может использовать JSON.Net, так или иначе неспособный использовать мой ответ? –

+0

Похоже, мы оба сбиты с места. Я надеялся, что найду способ очистить JSON без создания повторяющихся классов для всех ответов API (это упрощенный пример чего-то более сложного) с чем-то общим, который я могу использовать для анализа значений. – Kcoder

+0

@Kcoder Я сделал редактирование –

1

Ваш JSON содержит литеральную строку для объектов "body", которые фактически встроены в двухсерийный JSON. Для десериализации это иерархии POCO без необходимости ввести промежуточное string Json суррогатное свойство внутри любых типов, у вас есть несколько вариантов:

  1. Вы можете препроцессировать ваш JSON с помощью LINQ to JSON и заменить буквенные "body" строк с их Разбираемые эквиваленты:

    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. Можно ввести общий JsonConverter для POCO, соответствующего каждый объект Body, который анализирует поступающую встроенный JSON строку буквальной в LINQ to JSON иерархии, то десериализует что:

    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); 
          var token = JToken.Parse(json); 
          using (var subReader = token.CreateReader()) 
           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(); 
        } 
    } 
    

    Затем используйте это нравится:

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

    Обратите внимание, что преобразователь проверяет входящий маркер JSON, является ли строка, и если нет, то десериализует непосредственно. Таким образом, преобразователь должен использоваться, когда "body" JSON является и не является двойным сериализованным.

Для тестирования я создал следующие целевые классы с использованием 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; } 
} 
Смежные вопросы