2016-07-13 5 views
1

Angenommen, ich habe folgende Modellklasse:Wie wird das JSON-Objekt basierend auf einer anderen JSON-Eigenschaft bedingt deserialisiert?

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

und ich folgende JSON in diese Klasse deserialisieren möge:

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

Ergebnis Objekt kann jedes Mal anders sein (eines von 6 Modellen) und seine Art hängt von der JSON-Eigenschaft type ab.

Ich habe benutzerdefinierte erstellt ActionResultConverter (JsonConverter Anmerkung oben Result Eigenschaft Action Klasse) in der Lage sein sollte spezifischeresultObjekt basierend auf Zeichenfolge in type Eigenschaft von JSON zu erstellen.

Mein Problem ist, dass ich nicht weiß, wie auf diese Eigenschaft von Konverter zugreifen, da nur der result Teil des gesamten JSON an JsonReader übergeben wird.

Irgendwelche Ideen oder Hilfe werden geschätzt.

Danke!

+1

Sie könnten eine Klasse Deserialisieren in welche die json Struktur übereinstimmt, und verwenden Sie diese Klasse, um herauszufinden, welche Art von Benutzerdefinierte Klasse, in der die Daten enden sollen. Oder verwenden Sie dynamic. Hühner-Speck-Ranch. –

+0

Außerdem ist Ihre Eigenschaft 'type' offen. Ihr Enum-Wert für "open" ist 0. Strings zu Ints konvertieren nicht sehr gut. Wenn Sie in eine Klasse deserialisiert haben, die der JSON-Struktur entspricht, und in Ihre Endspielklasse konvertieren, könnte diese Konvertierung auch die sein, bei der Sie von String in Int (Enum) konvertieren. –

+1

Außerdem haben Sie Ihre enum die gleiche wie eine beliebte Systemklasse benannt: https://msdn.microsoft.com/en-us/library/system.type(v=vs.110).aspx Wird wahrscheinlich verwirrend sein einige, die das System pflegen. –

Antwort

5

Json.NET bietet keine Methode zum Zugriff auf den Wert einer Eigenschaft eines übergeordneten Objekts in der JSON-Hierarchie, während ein untergeordnetes Objekt deserialisiert wird. Wahrscheinlich liegt das daran, dass ein JSON-Objekt als ungeordneter Satz von Name/Wert-Paaren gemäß der standard definiert ist, so dass es keine Garantie geben kann, dass die gewünschte Elterneigenschaft vor dem Kind im JSON-Stream auftritt.

So anstatt die Type Eigenschaft in einem Konverter für ActionResult Handhabung, müssen Sie es in einem Konverter für Action selbst tun:

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

Beachten Sie die Verwendung von JsonSerializer.Populate() innerhalb ReadJson(). Dies fügt automatisch alle Eigenschaften von Action ein, die nicht Result sind, und vermeidet die manuelle Deserialisierung von jedem.

3

von http://json.codeplex.com/discussions/56031 inspiriert:

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

Editor Coded, so sorry für Fehler :)

+0

Downvoter, möchten Sie Ihre Gedanken teilen? –

Verwandte Themen