2016-07-13 4 views
1

Предположим, что у меня есть следующий класс модели:Как условно десериализовать объект JSON на основе другого свойства JSON?

public class Action 
{ 
    public enum Type 
    { 
     Open, 
     Close, 
     Remove, 
     Delete, 
     Reverse, 
     Alert, 
     ScaleInOut, 
     Nothing 
    } 

    [JsonProperty("id")] 
    public string Id { get; set; } 

    [JsonProperty("name")] 
    public string Name { get; set; } 

    [JsonProperty("active")] 
    [JsonConverter(typeof(IntToBoolConverter))] 
    public bool Active { get; set; } 

    [JsonProperty("type")] 
    [JsonConverter(typeof(ActionTypeConverter))] 
    public Type ActionType { get; set; } 

    [JsonProperty("result")] 
    [JsonConverter(typeof(ActionResultConverter))] 
    public ActionResult Result { get; set; } 
} 

и я десериализации следующий JSON в этом классе:

{ 
    "name":"test1", 
    "id":"aa0832f0508bb580ce7f0506132c1c13", 
    "active":"1", 
    "type":"open", 
    "result":{ 
     "property1":"buy", 
     "property2":"123.123", 
     "property3":"2016-07-16T23:00:00", 
     "property4":"768", 
     "property5":true 
    } 
} 

объект Результат может быть разным каждый раз (один из 6 моделей) и его тип зависит от свойства JSON type.

Я создал пользовательские ActionResultConverter (JsonConverter аннотации выше Result свойства Action класса), которые должны быть в состоянии создать конкретныеresultобъекта на основе строки в свойстве type JSON.

Моя проблема заключается в том, что я не знаю, как получить доступ к этому свойству из конвертера, потому что только часть result10 всего JSON передается JsonReader.

Любые идеи или помощь будут оценены.

Спасибо!

+1

можно десериализации в класс, который соответствует структуре JSon, и использовать этот класс, чтобы выяснить, какой тип пользовательский класс, в котором вы хотите получить данные. Или используйте динамический. Ранчо из куриного бекона. –

+0

Кроме того, ваше свойство 'type' является' open'. Ваше значение enum для 'open' равно 0. Строки для ints не очень хорошо конвертируются. Если вы deserialized в класс, который соответствует структуре json, а затем конвертируйте в свой класс конечной игры, это преобразование также может быть там, где вы конвертируете из строки в int (Enum). –

+1

Кроме того, вы назвали ваше перечисление таким же, как и в популярном системном классе: https://msdn.microsoft.com/en-us/library/system.type(v=vs.110).aspx Вероятно, некоторые из которых поддерживают систему. –

ответ

5

Json.NET не предоставляет способ доступа к значению свойства родительского объекта в иерархии JSON при десериализации дочернего объекта. Вероятно, это связано с тем, что объект JSON определяется как неупорядоченный набор пар имя/значение, в соответствии с standard, поэтому не может быть гарантии, что желаемое родительское свойство возникает перед дочерним элементом в потоке JSON.

Таким образом, вместо обработки Type собственности в конвертере для ActionResult, вы должны сделать это в конвертере для самого Action:

[JsonConverter(typeof(ActionConverter))] 
public class Action 
{ 
    readonly static Dictionary<Type, System.Type> typeToSystemType; 
    readonly static Dictionary<System.Type, Type> systemTypeToType; 

    static Action() 
    { 
     typeToSystemType = new Dictionary<Type, System.Type> 
     { 
      { Type.Open, typeof(OpenActionResult) }, 
     }; 
     systemTypeToType = typeToSystemType.ToDictionary(p => p.Value, p => p.Key); 
    } 

    public static Type SystemTypeToType(System.Type systemType) 
    { 
     return systemTypeToType[systemType]; 
    } 

    public static System.Type TypeToSystemType(Type type) 
    { 
     return typeToSystemType[type]; 
    } 

    public enum Type 
    { 
     Open, 
     Close, 
     Remove, 
     Delete, 
     Reverse, 
     Alert, 
     ScaleInOut, 
     Nothing 
    } 

    [JsonProperty("id")] 
    public string Id { get; set; } 

    [JsonProperty("name")] 
    public string Name { get; set; } 

    [JsonProperty("active")] 
    [JsonConverter(typeof(IntToBoolConverter))] 
    public bool Active { get; set; } 

    [JsonProperty("type")] 
    [JsonConverter(typeof(ActionTypeConverter))] 
    public Type ActionType { get; set; } 

    [JsonProperty("result")] 
    public ActionResult Result { get; set; } 
} 

class ActionConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     throw new NotImplementedException(); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.Null) 
      return null; 
     var obj = JObject.Load(reader); 
     var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType); 
     var action = existingValue as Action ?? (Action)contract.DefaultCreator(); 

     // Remove the Result property for manual deserialization 
     var result = obj.GetValue("Result", StringComparison.OrdinalIgnoreCase).RemoveFromLowestPossibleParent(); 

     // Populate the remaining properties. 
     using (var subReader = obj.CreateReader()) 
     { 
      serializer.Populate(subReader, action); 
     } 

     // Process the Result property 
     if (result != null) 
      action.Result = (ActionResult)result.ToObject(Action.TypeToSystemType(action.ActionType)); 

     return action; 
    } 

    public override bool CanWrite { get { return false; } } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

public static class JsonExtensions 
{ 
    public static JToken RemoveFromLowestPossibleParent(this JToken node) 
    { 
     if (node == null) 
      return null; 
     var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault(); 
     if (contained != null) 
      contained.Remove(); 
     // Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should 
     if (node.Parent is JProperty) 
      ((JProperty)node.Parent).Value = null; 
     return node; 
    } 
} 

Обратите внимание на использование JsonSerializer.Populate() внутри ReadJson(). Это автоматически заполняет все свойства Action, отличные от Result, избегая необходимости ручной десериализации каждого из них.

3

Вдохновленный http://json.codeplex.com/discussions/56031:

public sealed class ActionModelConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(ActionModel).IsAssignableFrom(objectType); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JObject jObject = JObject.Load(reader); 
     ActionModel actionModel = new ActionModel(); 

     // TODO: Manually populate properties 
     actionModel.Id = (string)jObject["id"].ToObject<string>(); 

     var type = (ActionModel.Type)jObject["type"].ToObject<ActionModel.Type>(); 
     switch (type) 
     { 
      case ActionModel.Type.Open: 
      var actionResult = jObject["result"].ToObject<ActionOpenResult>(jsonSerializer); 

      default: 
      throw new JsonSerializationException($"Unsupported action type: '{type}'"); 
     } 

     actionModel.Result = actionResult; 

     return actionModel; 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Кодовый в редакторе, так что извините за опечатки :)

+0

Downvoter, поделитесь своими мыслями? –

Смежные вопросы