« JSON et CSharp » : différence entre les versions
De Banane Atomic
Aller à la navigationAller à la recherche
(3 versions intermédiaires par le même utilisateur non affichées) | |||
Ligne 18 : | Ligne 18 : | ||
</kode> | </kode> | ||
= [https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/how-to JsonSerializer] = | = [https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/how-to JsonSerializer] ([https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-overview System.Text.Json]) = | ||
{{info | Replacement of {{boxx|Newtonsoft.Json}}}} | {{info | Replacement of {{boxx|Newtonsoft.Json}}}} | ||
{{info | The library is built-in as part of the {{boxx|.NET Core 3.0}} shared framework.}} | |||
{{warn | Doesn't serialize {{boxx|Dictionary}} is key is not a string.}} | |||
<kode lang='cs'> | <kode lang='cs'> | ||
using System.Text.Json; | using System.Text.Json; | ||
Ligne 56 : | Ligne 59 : | ||
[JsonPropertyName("PropertyX")] | [JsonPropertyName("PropertyX")] | ||
public string Property1 { get; set; } | public string Property1 { get; set; } | ||
[JsonIgnore] | |||
public string Property2 { get; set; } | |||
} | } | ||
Ligne 339 : | Ligne 345 : | ||
} | } | ||
</kode> | </kode> | ||
Dernière version du 14 octobre 2024 à 14:45
Liens
- json2csharp, générer des classes C# à partir de JSON
Contexte
class MaClasse { public string Property1 { get; set; } public string Property2 { get; set; } } |
{ "Property1": "Value1", "Property2": "Value2" } |
JsonSerializer (System.Text.Json)
Replacement of Newtonsoft.Json |
The library is built-in as part of the .NET Core 3.0 shared framework. |
Doesn't serialize Dictionary is key is not a string. |
using System.Text.Json; var jsonSerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // write property names in camelCase DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, // don't write properties with null values Converters = { new JsonStringEnumConverter() } }; var json = JsonSerializer.Serialize(myObject, jsonSerializerOptions); var myObject = JsonSerializer.Deserialize<MyClass>(json, jsonSerializerOptions); |
Custom converter
public class DateTimeJsonConverter : JsonConverter<DateTime> { private const string dateTimeFormat = "dd_MM_yyyy"; public override DateTime Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => DateTime.Parse(reader.GetString()!, dateTimeFormat, CultureInfo.InvariantCulture); public override void Write( Utf8JsonWriter writer, DateTime dateTimeValue, JsonSerializerOptions options) => writer.WriteStringValue(dateTimeValue.ToString(dateTimeFormat, CultureInfo.InvariantCulture)); } |
Attributes
class MyClass { [JsonPropertyName("PropertyX")] public string Property1 { get; set; } [JsonIgnore] public string Property2 { get; set; } } |
JavaScriptSerializer
Pas de pretty print |
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
Now use JsonSerializer |
using Newtonsoft.Json; // lecture du fichier var jsonContent = File.ReadAllText("/chemin/vers/le/fichier.json"); // 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; } // Ne pas sérialiser cette propriété si sa valeur est nulle [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string P31 { get; set; } public IList<string> P32 { get; private set; } = new List<string>(); // Ne pas sérialiser cette propriété si la liste est vide public bool ShouldSerializeP32() { return P32.Count > 0; } // 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
// Affiche les types .NET dans le json généré var jsonSerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; JsonConvert.SerializeObject(data, Formatting.Indented, jsonSerializerSettings); |
{ "$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 |
{ "prop1": "value1", "prop2": "2.34", "prop3": [ { "sousprop1": "value11", "sousprop2": "11.34" }, { "sousprop1": "value12", "sousprop2": "12.34" } ] } |
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.
[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); } } |
// 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.
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
// é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>)); } |