C # JSON.NET – Deserializzare la response di una richiesta
Integrando le API di un portale mi sono trovato con l’esigenza di dover deserializzare un oggetto json complesso in un oggetto/classe C# da utilizzare all’interno del mio progetto, il json in questione ha all’interno delle liste/array innestate, di seguito un esempio del json:
{"serie":[ {"azione":"impression","data":[ ["2017-05-12",1],["2017-05-15",4],["2017-05-25",22],["2017-05-26",4],["2017-06-28",35],["2017-07-07",1] ] }, {"azione":"play","data":[ ["2017-05-12",0],["2017-05-15",0],["2017-05-25",0],["2017-05-26",0],["2017-06-28",0] ] }, {"azione":"custom","data":[ ["2017-05-12",2],["2017-05-15",1],["2017-05-26",7],["2017-07-11",27],["2017-07-13",27] ]} ] }
Ho avuto qualche problema a capire il modo più pulito (possibile) per deserializzare alcuni dati JSON, il primo tentativo è stato effettuare direttamente la deserializzazione al mio oggetto:
Newtonsoft.Json.JsonConvert.DeserializeObject<MyObject>(jsonData);
JsonConverter
Dopo una breve ricerca ho trovato la soluzione per deserializzare array json:
public class ObjectToArrayConverter<T> : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(T) == objectType; } static bool ShouldSkip(JsonProperty property) { return property.Ignored || !property.Readable || !property.Writable; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var type = value.GetType(); var contract = serializer.ContractResolver.ResolveContract(type) as JsonObjectContract; if (contract == null) throw new JsonSerializationException("invalid type " + type.FullName); var list = contract.Properties.Where(p => !ShouldSkip(p)).Select(p => p.ValueProvider.GetValue(value)); serializer.Serialize(writer, list); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var token = JToken.Load(reader); if (token.Type != JTokenType.Array) throw new JsonSerializationException("token was not an array"); var contract = serializer.ContractResolver.ResolveContract(objectType) as JsonObjectContract; if (contract == null) throw new JsonSerializationException("invalid type " + objectType.FullName); var value = existingValue ?? contract.DefaultCreator(); foreach (var pair in contract.Properties.Where(p => !ShouldSkip(p)).Zip(token, (p, v) => new { Value = v, Property = p })) { var propertyValue = pair.Value.ToObject(pair.Property.PropertyType, serializer); pair.Property.ValueProvider.SetValue(value, propertyValue); } return value; } }
Quindi aggiungere il convertitore alla classe e indicare l’ordine di ciascuna proprietà utilizzando JsonPropertyAttribute.Order:
[JsonConverter(typeof(ObjectToArrayConverter<SeriesData>))] public class SeriesData { [JsonProperty(Order = 1)] public string stringdata { get; set; } [JsonProperty(Order = 2)] public int conta { get; set; } }
Alla fine dichiarare l’oggetto:
public class ResponseResult { public List<Series> Series { get; set; } } public class Series { public string Action { get; set; } public List<SeriesData> Data { get; set; } }