JSON et CSharp

De Banane Atomic
Aller à la navigationAller à la recherche

Liens

  • json2csharp, générer des classes C# à partir de JSON

Contexte

Csharp.svg
class MaClasse
{
    public string Property1 { get; set; }
    public string Property2 { get; set; }
}
Javascript.svg
{
    "Property1": "Value1",
    "Property2": "Value2"
}

JavaScriptSerializer

Pas de pretty print
Csharp.svg
using System.Web.Script.Serialization; // System.Web.Extensions.dll

var mc = new MaClasse() { Property1 = "Value1", Property2 = "Value2" };

// sérialisation
string json = new JavaScriptSerializer().Serialize(mc);

// désérialisation
MaClasse mc2 = new JavaScriptSerializer().Deserialize<MaClasse>(json);

Newtonsoft.Json

Newtonsoft.Json est disponible via NuGet.
Csharp.svg
using Newtonsoft.Json;

// lecture du fichier
string text;
using(var streamReader = new StreamReader("/chemin/vers/le/fichier.json"))
{
    text = streamReader.ReadToEnd();
}

// Désérialiser
MaClasse monObjet = JsonConvert.DeserializeObject<MaClasse>(text);

// sérialiser avec pretty print
var json = JsonConvert.SerializeObject(monObjet, Formatting.Indented);


class MaClasse
{
    // Forcer cette propriété en deuxième position
    [JsonProperty(Order = 2)]
    public string P1 { get; set; }

    // Forcer cette propriété en première position
    [JsonProperty(Order = 1)]
    public string P2 { get; set; }

    // Ne pas sérialiser cette propriété
    [JsonIgnore]
    public string P3 { get; set; }

    // mapping de la propriété avec odata.p4 en sérialisation et désérialisation
    [JsonProperty(PropertyName = "odata.p4")]
    public string P4 { get; set; }
L'ordre par défaut est de -1

JsonSerializerSettings

Csharp.svg
// Affiche les types .NET dans le json généré
var jsonSerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All };
JsonConvert.SerializeObject(data, Formatting.Indented, jsonSerializerSettings);
Javascript.svg
{
  "$type": "System.Collections.Generic.List`1[[MonNamespace.MaClasse, MonAssembly]], mscorlib",
  "$values": [
    {
      "$type": "MonNamespace.MaClasse, MonAssembly",
      "MaPropriété": "valeur"
    }
  ]
}

JObject

JObject collection de JProperties
JProperties contient un Name string et une Value JToken
JToken valeur JSON générique: string, JArray, JObject, JProperty
Javascript.svg
{
  "prop1": "value1",
  "prop2": "2.34",
  "prop3": [
    {
      "sousprop1": "value11",
      "sousprop2": "11.34"
    },
    {
      "sousprop1": "value12",
      "sousprop2": "12.34"
    }
  ]
}
Csharp.svg
JObject jo = JObject.Parse(json);
string jsonText = jo.ToString();

// dynamic
dynamic d = JObject.Parse(json);
d.prop2;  // 2.34

// Parse et ToObject
JArray jArray = JArray.Parse(json);
List<MyClass> myObjects = jArray.ToObject<List<MyClass>>();

// path
string value11 = jo.SelectToken("prop3[0].sousprop1").Value<string>();  // value11
string value11 = (string)jo.SelectToken("prop3[0].sousprop1");  // cast au lieu du Value<T>()

// élément du tableau prop3 correspondant au filtre (JObject)
JToken token = jo.SelectToken("$.prop3[?(@.sousprop1 == 'value12')]");

// élément du tableau prop3 correspondant au filtre (JObject)
JToken token = jo.SelectToken("$.prop3[?(@.sousprop2 > 12)]");
// JSONException si plusieurs éléments correspondent au filtre

// éléments du tableau prop3 correspondant au filtre (JObject)
IEnumerable<JToken> tokens = jo.SelectTokens("$.prop3[?(@.sousprop2 > 11)]");
// toutes les sousprop2 filles de prop3
IEnumerable<JToken> tokens = jo.SelectTokens("$.prop3[?(@.sousprop2)].sousprop2");

JToken token = jo.SelectToken("$.prop3[?(@.sousprop1 == 'value12')].sousprop2");  // 12.34
token.Replace("13");

foreach (JProperty property in jo.Properties())
{
    string name = property.Name;  // prop1
    JToken valueToken = property.Value;
    if (valueToken.Type == JTokenType.String)
    {
        string valueString = valueToken.Value<string>();  // value1
    }
    else if (valueToken.Type == JTokenType.Array)
    {
        JArray valueArray = valueToken.Value<JArray>();
        foreach (JObject sousJo in valueArray.Children<JObject>())
        {
            foreach (JProperty sousProperty in sousJo.Properties())
            {
                string sousName = sousProperty.Name;  // sousprop1
                JToken sousValueToken = sousProperty.Value;
                if (sousValueToken.Type == JTokenType.String)
                {
                    string sousValueString = sousValueToken.Value<string>();  // value11
                }
            }
        }
    }
}

JsonConverter

Permet de modifier le comportement de sérialisation.

Csharp.svg
[JsonConverter(typeof(MyListJsonConverter))]
public class MyList
{
    public int Prop1 { get; set; }
    public int Prop2 { get; set; }
}

public class MyListJsonConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var myList = (MyList) value;
        serializer.Serialize(writer, new int[] { myList.Prop1, myList.Prop2 });
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var ml = new MyList();

        if (reader.TokenType != JsonToken.Null)
        {
            if (reader.TokenType == JsonToken.StartArray)
            {
                JToken token = JToken.Load(reader);
                var values = token.ToObject<List<int>>();
                ml.Prop1 = values[0];
                ml.Prop2 = values[1];
            }
        }

        return ml;
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(MyList).IsAssignableFrom(objectType);
    }
}
Json.svg
// sérialisation custom
[
  [ 1, 11 ],
  [ 2, 22 ]
]

// sérialisation par défaut
[
  { "Prop1": 1, "Prop2": 11 },
  { "Prop1": 2, "Prop2": 22 },
]

Désérialiser dans une classe abstraite

On souhaite désérialiser dans une classe abstraite, mais le mécanisme de désérialisation ne peut créer cette classe abstraite.
Il faut donc surcharger la méthode ReadJson du JsonConverter pour lui permettre de choisir quelle classe concrète doit être utilisée.

Csharp.svg
public abstract class A
{
    public abstract string P1 { get; set; }
}

public class B : A
{
    public override string P1 { ... }
}

public class C : A
{
    public override string P1 { ... }
}

public class AJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(A).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject item = JObject.Load(reader);
        if (item["P1"].Value<string>() == "...")
        {
            return item.ToObject<B>();
        }
        else
        {
            return item.ToObject<C>();
        }
    }

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

var monObjet = JsonConvert.DeserializeObject<A>(jsonText, new AJsonConverter());

Fichier JSON

Csharp.svg
// écriture des items dans jsonFilePath
File.WriteAllText(jsonFilePath, JsonConvert.SerializeObject(items, Formatting.Indented));

// lecture de jsonFilePath dans items
using (StreamReader file = File.OpenText(jsonFilePath))
{
    var serializer = new JsonSerializer();
    items = (List<Item>)serializer.Deserialize(file, typeof(List<Item>));
}