C # JSON.NET – Deserializzare la response che utilizza una struttura di dati insolita

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