Aller au contenu

« JSON et CSharp » : différence entre les versions

De Banane Atomic
 
(18 versions intermédiaires par le même utilisateur non affichées)
Ligne 1 : Ligne 1 :
[[Category:CSharp]]
[[Category:CSharp]]
= Liens =
= Links =
* [http://json2csharp.com/ json2csharp], générer des classes C# à partir de JSON
* [http://json2csharp.com/ json2csharp], générer des classes C# à partir de JSON
* [[String#Raw_string_literals|Raw string literal]]


= Contexte =
= Contexte =
Ligne 18 : Ligne 19 :
</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;
var jsonSerializerOptions = new JsonSerializerOptions
var jsonSerializerOptions = new JsonSerializerOptions
{
{
Ligne 34 : Ligne 40 :
== [https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to Custom converter] ==
== [https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to Custom converter] ==
<kode lang='cs'>
<kode lang='cs'>
public class DateTimeOffsetJsonConverter : JsonConverter<DateTimeOffset>
public class DateTimeJsonConverter : JsonConverter<DateTime>
{
{
     public override DateTimeOffset Read(
    private const string dateTimeFormat = "dd_MM_yyyy";
 
     public override DateTime Read(
         ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
         ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
         => DateTimeOffset.Parse(reader.GetString()!);
         => DateTime.Parse(reader.GetString()!, dateTimeFormat, CultureInfo.InvariantCulture);


     public override void Write(
     public override void Write(
         Utf8JsonWriter writer, DateTimeOffset dateTimeValue, JsonSerializerOptions options)
         Utf8JsonWriter writer, DateTime dateTimeValue, JsonSerializerOptions options)
         => writer.WriteStringValue(dateTimeValue.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture));
         => writer.WriteStringValue(dateTimeValue.ToString(dateTimeFormat, CultureInfo.InvariantCulture));
}
</kode>
 
== Attributes ==
<kode lang='cs'>
class MyClass
{
    [JsonPropertyName("PropertyX")]
    public string Property1 { get; set; }
 
    [JsonIgnore]
    public string Property2 { get; set; }
}
</kode>
 
== JsonDocument ==
<kode lang='cs'>
// get the json element value of a property
if (jsonDocument.RootElement.TryGetProperty(propertyName, out JsonElement jsonElement)) { }
 
// get the string value of a JsonDocument
string stringJsonDocument = jsonDocument.RootElement.GetRawText();
 
// create a JsonDocument from a json string
JsonDocument jsonDocument = JsonDocument.Parse(stringJsonDocument);
 
// create an updated JsonDocument by updating the value of a property
string stringJsonDocument = jsonDocument.RootElement.GetRawText();
JsonObject? jsonObjectDocument = JsonNode.Parse(stringJsonDocument)?.AsObject();
 
var serializedValue = JsonSerializer.Serialize(value, jsonSerializerOptions);
JsonNode? jsonNodeSerializedValue = JsonNode.Parse(serializedValue);
jsonObjectDocument[propertyName] = jsonNodeSerializedValue;
 
string updatedStringJsonDocument = jsonObjectDocument.ToJsonString();
JsonDocument updatedJsonDocument = JsonDocument.Parse(updatedStringJsonDocument);
</kode>
 
=== Extension methods ===
<kode lang='cs' collapsed>
public static bool TryDeserializeProperty<T>(
    this JsonDocument jsonDocument,
    string propertyName,
    JsonSerializerOptions jsonSerializerOptions,
    out T? value)
{
    value = default(T) ?? CreateInstance<T>();
 
    if (jsonDocument.RootElement.ValueKind == JsonValueKind.Object
        && jsonDocument.RootElement.TryGetProperty(propertyName, out var jsonElement))
    {
        value = jsonElement.Deserialize<T>(jsonSerializerOptions)!;
        return true;
    }
 
    return false;
 
    static TInstance? CreateInstance<TInstance>(int arraySize = 0)
    {
        var type = typeof(TInstance);
 
        if (type.IsArray)
        {
            var elementType = type.GetElementType()!;
            return (TInstance)(object)Array.CreateInstance(elementType, arraySize);
        }
 
        if (type == typeof(string))
        {
            return default;
        }
 
        return (TInstance?)Activator.CreateInstance(type);
    }
}
 
public static JsonObject? ToJsonObject(this JsonDocument jsonDocument)
{
    var stringJsonDocument = jsonDocument.RootElement.GetRawText();
    var jsonObjectDocument = JsonNode.Parse(stringJsonDocument)?.AsObject();
 
    return jsonObjectDocument;
}
 
public static JsonDocument ToJsonDocument(this JsonObject jsonObject)
{
    var stringJsonObject = jsonObject.ToJsonString();
    var jsonDocument = JsonDocument.Parse(stringJsonObject);
 
    return jsonDocument;
}
 
public static JsonObject UpsertProperty<T>(
    this JsonObject jsonObject,
    string propertyName,
    T value,
    JsonSerializerOptions jsonSerializerOptions)
{
    var serializedValue = JsonSerializer.Serialize(value, jsonSerializerOptions);
    var jsonNodeSerializedValue = JsonNode.Parse(serializedValue);
    jsonObject[propertyName] = jsonNodeSerializedValue;
 
    return jsonObject;
}
}
</kode>
</kode>
Ligne 326 : Ligne 437 :
</kode>
</kode>


= [https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-overview System.Text.Json] =
== ASP.NET Core Web API integration ==
{{info | The library is built-in as part of the {{boxx|.NET Core 3.0}} shared framework.}}
<filebox fn='Program.cs'>
{{warn | Doesn't serialize {{boxx|Dictionary}} is key is not a string.}}
builder.Services
    .AddControllers()
    .AddNewtonsoftJson(x => x.SerializerSettings.Converters.Add(new StringEnumConverter())); // handle enums
 
builder.Services.AddSwaggerGenNewtonsoftSupport(); // dislay the enum value as string instead of int (assembly Swashbuckle.AspNetCore.Newtonsoft)
</filebox>

Dernière version du 28 février 2025 à 17:47

Links

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

JsonDocument

// get the json element value of a property
if (jsonDocument.RootElement.TryGetProperty(propertyName, out JsonElement jsonElement)) { }

// get the string value of a JsonDocument
string stringJsonDocument = jsonDocument.RootElement.GetRawText();

// create a JsonDocument from a json string
JsonDocument jsonDocument = JsonDocument.Parse(stringJsonDocument);

// create an updated JsonDocument by updating the value of a property
string stringJsonDocument = jsonDocument.RootElement.GetRawText();
JsonObject? jsonObjectDocument = JsonNode.Parse(stringJsonDocument)?.AsObject();

var serializedValue = JsonSerializer.Serialize(value, jsonSerializerOptions);
JsonNode? jsonNodeSerializedValue = JsonNode.Parse(serializedValue);
jsonObjectDocument[propertyName] = jsonNodeSerializedValue;

string updatedStringJsonDocument = jsonObjectDocument.ToJsonString();
JsonDocument updatedJsonDocument = JsonDocument.Parse(updatedStringJsonDocument);

Extension methods

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

ASP.NET Core Web API integration

Program.cs
builder.Services
    .AddControllers()
    .AddNewtonsoftJson(x => x.SerializerSettings.Converters.Add(new StringEnumConverter())); // handle enums

builder.Services.AddSwaggerGenNewtonsoftSupport(); // dislay the enum value as string instead of int (assembly Swashbuckle.AspNetCore.Newtonsoft)