Come usare un modello DOM (Document Object Model) JSON in System.Text.Json
Questo articolo illustra come usare un modello DOM (Document Object Model) JSON per l'accesso casuale ai dati in un payload JSON.
Scelte per il modello DOM JSON
L'uso di un modello DOM è un'alternativa alla deserializzazione con JsonSerializer quando:
- Non è disponibile un tipo di destinazione della deserializzazione.
- Il codice JSON ricevuto non ha uno schema fisso e deve essere controllato per sapere cosa contiene.
System.Text.Json
offre due modi per creare un modello DOM JSON:
JsonDocument consente di creare un modello DOM di sola lettura tramite
Utf8JsonReader
. È possibile accedere agli elementi JSON che compongono il payload tramite il tipo JsonElement. Il tipoJsonElement
fornisce gli enumeratori di matrice e oggetto JSON insieme alle API per convertire il testo JSON in tipi .NET comuni.JsonDocument
espone una proprietà RootElement. Per ottenere ulteriori informazioni, consultare la sezione Utilizzare JsonDocument più avanti in questo articolo.JsonNode e le classi derivate nello spazio dei nomi System.Text.Json.Nodes offrono la possibilità di creare un modello DOM modificabile. È possibile accedere agli elementi JSON che compongono il payload tramite i tipi JsonNode, JsonObject, JsonArray, JsonValue e JsonElement. Per ottenere ulteriori informazioni, consultare la sezione Usare
JsonNode
più avanti in questo articolo.
Per la scelta tra JsonDocument
e JsonNode
, tenere presenti i fattori seguenti:
- Il modello DOM
JsonNode
può essere modificato dopo la creazione. Il modello DOMJsonDocument
non è modificabile. - Il modello DOM
JsonDocument
consente un accesso più rapido ai dati.
Utilizzare JsonNode
.
Nell'esempio seguente viene illustrato come usare JsonNode e gli altri tipi nello spazio dei nomi System.Text.Json.Nodes per:
- Creare un modello DOM da una stringa JSON
- Scrivere un codice JSON da un modello DOM.
- Ottenere un valore, un oggetto o una matrice da un modello DOM.
using System.Text.Json;
using System.Text.Json.Nodes;
namespace JsonNodeFromStringExample;
public class Program
{
public static void Main()
{
string jsonString = """
{
"Date": "2019-08-01T00:00:00",
"Temperature": 25,
"Summary": "Hot",
"DatesAvailable": [
"2019-08-01T00:00:00",
"2019-08-02T00:00:00"
],
"TemperatureRanges": {
"Cold": {
"High": 20,
"Low": -10
},
"Hot": {
"High": 60,
"Low": 20
}
}
}
""";
// Create a JsonNode DOM from a JSON string.
JsonNode forecastNode = JsonNode.Parse(jsonString)!;
// Write JSON from a JsonNode
var options = new JsonSerializerOptions { WriteIndented = true };
Console.WriteLine(forecastNode!.ToJsonString(options));
// output:
//{
// "Date": "2019-08-01T00:00:00",
// "Temperature": 25,
// "Summary": "Hot",
// "DatesAvailable": [
// "2019-08-01T00:00:00",
// "2019-08-02T00:00:00"
// ],
// "TemperatureRanges": {
// "Cold": {
// "High": 20,
// "Low": -10
// },
// "Hot": {
// "High": 60,
// "Low": 20
// }
// }
//}
// Get value from a JsonNode.
JsonNode temperatureNode = forecastNode!["Temperature"]!;
Console.WriteLine($"Type={temperatureNode.GetType()}");
Console.WriteLine($"JSON={temperatureNode.ToJsonString()}");
//output:
//Type = System.Text.Json.Nodes.JsonValue`1[System.Text.Json.JsonElement]
//JSON = 25
// Get a typed value from a JsonNode.
int temperatureInt = (int)forecastNode!["Temperature"]!;
Console.WriteLine($"Value={temperatureInt}");
//output:
//Value=25
// Get a typed value from a JsonNode by using GetValue<T>.
temperatureInt = forecastNode!["Temperature"]!.GetValue<int>();
Console.WriteLine($"TemperatureInt={temperatureInt}");
//output:
//Value=25
// Get a JSON object from a JsonNode.
JsonNode temperatureRanges = forecastNode!["TemperatureRanges"]!;
Console.WriteLine($"Type={temperatureRanges.GetType()}");
Console.WriteLine($"JSON={temperatureRanges.ToJsonString()}");
//output:
//Type = System.Text.Json.Nodes.JsonObject
//JSON = { "Cold":{ "High":20,"Low":-10},"Hot":{ "High":60,"Low":20} }
// Get a JSON array from a JsonNode.
JsonNode datesAvailable = forecastNode!["DatesAvailable"]!;
Console.WriteLine($"Type={datesAvailable.GetType()}");
Console.WriteLine($"JSON={datesAvailable.ToJsonString()}");
//output:
//datesAvailable Type = System.Text.Json.Nodes.JsonArray
//datesAvailable JSON =["2019-08-01T00:00:00", "2019-08-02T00:00:00"]
// Get an array element value from a JsonArray.
JsonNode firstDateAvailable = datesAvailable[0]!;
Console.WriteLine($"Type={firstDateAvailable.GetType()}");
Console.WriteLine($"JSON={firstDateAvailable.ToJsonString()}");
//output:
//Type = System.Text.Json.Nodes.JsonValue`1[System.Text.Json.JsonElement]
//JSON = "2019-08-01T00:00:00"
// Get a typed value by chaining references.
int coldHighTemperature = (int)forecastNode["TemperatureRanges"]!["Cold"]!["High"]!;
Console.WriteLine($"TemperatureRanges.Cold.High={coldHighTemperature}");
//output:
//TemperatureRanges.Cold.High = 20
// Parse a JSON array
var datesNode = JsonNode.Parse(@"[""2019-08-01T00:00:00"",""2019-08-02T00:00:00""]");
JsonNode firstDate = datesNode![0]!.GetValue<DateTime>();
Console.WriteLine($"firstDate={ firstDate}");
//output:
//firstDate = "2019-08-01T00:00:00"
}
}
Creare un modello DOM JsonNode con inizializzatori di oggetti e apportare modifiche
L'esempio seguente illustra come:
- Creare un modello DOM usando gli inizializzatori di oggetti.
- Apportare modifiche a un modello DOM.
using System.Text.Json;
using System.Text.Json.Nodes;
namespace JsonNodeFromObjectExample;
public class Program
{
public static void Main()
{
// Create a new JsonObject using object initializers.
var forecastObject = new JsonObject
{
["Date"] = new DateTime(2019, 8, 1),
["Temperature"] = 25,
["Summary"] = "Hot",
["DatesAvailable"] = new JsonArray(
new DateTime(2019, 8, 1), new DateTime(2019, 8, 2)),
["TemperatureRanges"] = new JsonObject
{
["Cold"] = new JsonObject
{
["High"] = 20,
["Low"] = -10
}
},
["SummaryWords"] = new JsonArray("Cool", "Windy", "Humid")
};
// Add an object.
forecastObject!["TemperatureRanges"]!["Hot"] =
new JsonObject { ["High"] = 60, ["Low"] = 20 };
// Remove a property.
forecastObject.Remove("SummaryWords");
// Change the value of a property.
forecastObject["Date"] = new DateTime(2019, 8, 3);
var options = new JsonSerializerOptions { WriteIndented = true };
Console.WriteLine(forecastObject.ToJsonString(options));
//output:
//{
// "Date": "2019-08-03T00:00:00",
// "Temperature": 25,
// "Summary": "Hot",
// "DatesAvailable": [
// "2019-08-01T00:00:00",
// "2019-08-02T00:00:00"
// ],
// "TemperatureRanges": {
// "Cold": {
// "High": 20,
// "Low": -10
// },
// "Hot": {
// "High": 60,
// "Low": 20
// }
// }
//}
}
}
Deserializzare le sottosezioni di un payload JSON
L'esempio seguente illustra come usare JsonNode per passare a una sottosezione di un albero JSON e deserializzare un singolo valore, un tipo personalizzato o una matrice da tale sottosezione.
using System.Text.Json;
using System.Text.Json.Nodes;
namespace JsonNodePOCOExample;
public class TemperatureRanges : Dictionary<string, HighLowTemps>
{
}
public class HighLowTemps
{
public int High { get; set; }
public int Low { get; set; }
}
public class Program
{
public static DateTime[]? DatesAvailable { get; set; }
public static void Main()
{
string jsonString = """
{
"Date": "2019-08-01T00:00:00",
"Temperature": 25,
"Summary": "Hot",
"DatesAvailable": [
"2019-08-01T00:00:00",
"2019-08-02T00:00:00"
],
"TemperatureRanges": {
"Cold": {
"High": 20,
"Low": -10
},
"Hot": {
"High": 60,
"Low": 20
}
}
}
""";
// Parse all of the JSON.
JsonNode forecastNode = JsonNode.Parse(jsonString)!;
// Get a single value
int hotHigh = forecastNode["TemperatureRanges"]!["Hot"]!["High"]!.GetValue<int>();
Console.WriteLine($"Hot.High={hotHigh}");
// output:
//Hot.High=60
// Get a subsection and deserialize it into a custom type.
JsonObject temperatureRangesObject = forecastNode!["TemperatureRanges"]!.AsObject();
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
temperatureRangesObject.WriteTo(writer);
writer.Flush();
TemperatureRanges? temperatureRanges =
JsonSerializer.Deserialize<TemperatureRanges>(stream.ToArray());
Console.WriteLine($"Cold.Low={temperatureRanges!["Cold"].Low}, Hot.High={temperatureRanges["Hot"].High}");
// output:
//Cold.Low=-10, Hot.High=60
// Get a subsection and deserialize it into an array.
JsonArray datesAvailable = forecastNode!["DatesAvailable"]!.AsArray()!;
Console.WriteLine($"DatesAvailable[0]={datesAvailable[0]}");
// output:
//DatesAvailable[0]=8/1/2019 12:00:00 AM
}
}
Esempio di voto medio JsonNode
Nell'esempio seguente viene selezionata una matrice JSON con valori interi e viene calcolato un valore medio:
using System.Text.Json.Nodes;
namespace JsonNodeAverageGradeExample;
public class Program
{
public static void Main()
{
string jsonString = """
{
"Class Name": "Science",
"Teacher\u0027s Name": "Jane",
"Semester": "2019-01-01",
"Students": [
{
"Name": "John",
"Grade": 94.3
},
{
"Name": "James",
"Grade": 81.0
},
{
"Name": "Julia",
"Grade": 91.9
},
{
"Name": "Jessica",
"Grade": 72.4
},
{
"Name": "Johnathan"
}
],
"Final": true
}
""";
double sum = 0;
JsonNode document = JsonNode.Parse(jsonString)!;
JsonNode root = document.Root;
JsonArray studentsArray = root["Students"]!.AsArray();
int count = studentsArray.Count;
foreach (JsonNode? student in studentsArray)
{
if (student?["Grade"] is JsonNode gradeNode)
{
sum += (double)gradeNode;
}
else
{
sum += 70;
}
}
double average = sum / count;
Console.WriteLine($"Average grade : {average}");
}
}
// output:
//Average grade : 81.92
Il codice precedente:
- Calcola un voto medio per gli oggetti in una matrice
Students
con una proprietàGrade
. - Assegna un voto predefinito pari a 70 per gli studenti che non hanno un voto.
- Ottiene il numero di studenti dalla proprietà
Count
diJsonArray
.
JsonNode
con JsonSerializerOptions
È possibile usare JsonSerializer
per serializzare e deserializzare un'istanza di JsonNode
. Tuttavia, se si usa un overload che accetta JsonSerializerOptions
, l'istanza delle opzioni viene usata solo per ottenere convertitori personalizzati. Le altre funzionalità dell'istanza delle opzioni non vengono usate. Ad esempio, se si imposta JsonSerializerOptions.DefaultIgnoreCondition su WhenWritingNull e si chiama JsonSerializer
con un overload che accetta JsonSerializerOptions
, le proprietà Null non verranno ignorate.
La stessa limitazione si applica ai metodi JsonNode
che accettano un parametro JsonSerializerOptions
: WriteTo(Utf8JsonWriter, JsonSerializerOptions) e ToJsonString(JsonSerializerOptions). Queste API usano JsonSerializerOptions
solo per ottenere convertitori personalizzati.
Nell'esempio seguente viene illustrato il risultato dell'utilizzo di metodi che accettano un parametro JsonSerializerOptions
e serializzano un'istanza di JsonNode
:
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace JsonNodeWithJsonSerializerOptions;
public class Program
{
public static void Main()
{
Person person = new() { Name = "Nancy" };
// Default serialization - Address property included with null token.
// Output: {"Name":"Nancy","Address":null}
string personJsonWithNull = JsonSerializer.Serialize(person);
Console.WriteLine(personJsonWithNull);
// Serialize and ignore null properties - null Address property is omitted
// Output: {"Name":"Nancy"}
JsonSerializerOptions options = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
string personJsonWithoutNull = JsonSerializer.Serialize(person, options);
Console.WriteLine(personJsonWithoutNull);
// Ignore null properties doesn't work when serializing JsonNode instance
// by using JsonSerializer.
// Output: {"Name":"Nancy","Address":null}
JsonNode? personJsonNode = JsonSerializer.Deserialize<JsonNode>(personJsonWithNull);
personJsonWithNull = JsonSerializer.Serialize(personJsonNode, options);
Console.WriteLine(personJsonWithNull);
// Ignore null properties doesn't work when serializing JsonNode instance
// by using JsonNode.ToJsonString method.
// Output: {"Name":"Nancy","Address":null}
personJsonWithNull = personJsonNode!.ToJsonString(options);
Console.WriteLine(personJsonWithNull);
// Ignore null properties doesn't work when serializing JsonNode instance
// by using JsonNode.WriteTo method.
// Output: {"Name":"Nancy","Address":null}
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
personJsonNode!.WriteTo(writer, options);
writer.Flush();
personJsonWithNull = Encoding.UTF8.GetString(stream.ToArray());
Console.WriteLine(personJsonWithNull);
}
}
public class Person
{
public string? Name { get; set; }
public string? Address { get; set; }
}
Se sono necessarie funzionalità JsonSerializerOptions
diverse dai convertitori personalizzati, usare JsonSerializer
con destinazioni fortemente tipizzate (come la classe Person
in questo esempio), anziché JsonNode
.
Modificare l'ordine delle proprietà
JsonObject è uno degli elementi nel payload di un JsonNodeoggetto e rappresenta un oggetto JSON modificabile. Anche se il tipo è modellato come IDictionary<string, JsonNode>
, dove ogni voce è una proprietà dell'oggetto, incapsula un ordine di proprietà implicito. Tuttavia, le API come Insert(Int32, String, JsonNode) e RemoveAt(Int32) modellano efficacemente il tipo come dizionario ordinato consentendo di inserire e rimuovere elementi in un indice specifico. Queste API consentono modifiche alle istanze di oggetti che possono influenzare direttamente l'ordine delle proprietà.
Il codice seguente mostra un esempio di aggiunta o spostamento di una proprietà specifica all'inizio dell'oggetto.
var schema = (JsonObject)JsonSerializerOptions.Default.GetJsonSchemaAsNode(typeof(MyPoco));
JsonNode? idValue;
switch (schema.IndexOf("$id"))
{
// $id property missing.
case < 0:
idValue = (JsonNode)"https://example.com/schema";
schema.Insert(0, "$id", idValue);
break;
// $id property already at the start of the object.
case 0:
break;
// $id exists but not at the start of the object.
case int index:
idValue = schema[index];
schema.RemoveAt(index);
schema.Insert(0, "$id", idValue);
break;
}
Confrontare JsonNodes
Per confrontare due JsonNode
oggetti per verificarne l'uguaglianza, inclusi i relativi elementi discendenti, usare il JsonNode.DeepEquals(JsonNode, JsonNode) metodo .
Utilizzare JsonDocument
.
L'esempio seguente illustra come usare la classe JsonDocument per l'accesso casuale ai dati in una stringa JSON:
double sum = 0;
int count = 0;
using (JsonDocument document = JsonDocument.Parse(jsonString))
{
JsonElement root = document.RootElement;
JsonElement studentsElement = root.GetProperty("Students");
foreach (JsonElement student in studentsElement.EnumerateArray())
{
if (student.TryGetProperty("Grade", out JsonElement gradeElement))
{
sum += gradeElement.GetDouble();
}
else
{
sum += 70;
}
count++;
}
}
double average = sum / count;
Console.WriteLine($"Average grade : {average}");
Dim sum As Double = 0
Dim count As Integer = 0
Using document As JsonDocument = JsonDocument.Parse(jsonString)
Dim root As JsonElement = document.RootElement
Dim studentsElement As JsonElement = root.GetProperty("Students")
For Each student As JsonElement In studentsElement.EnumerateArray()
Dim gradeElement As JsonElement = Nothing
If student.TryGetProperty("Grade", gradeElement) Then
sum += gradeElement.GetDouble()
Else
sum += 70
End If
count += 1
Next
End Using
Dim average As Double = sum / count
Console.WriteLine($"Average grade : {average}")
Il codice precedente:
- Si supponga che il codice JSON da analizzare si trovi in una stringa denominata
jsonString
. - Calcola un voto medio per gli oggetti in una matrice
Students
con una proprietàGrade
. - Assegna un voto predefinito pari a 70 per gli studenti che non hanno un voto.
- Crea l'istanza di
JsonDocument
in un'istruzioneusing
perchéJsonDocument
implementaIDisposable
. Dopo l'eliminazione di un'istanza diJsonDocument
, si perde anche l'accesso a tutte le relative istanzeJsonElement
. Per mantenere l'accesso a un'istanza diJsonElement
, crearne una copia prima dell'eliminazione dell'istanza padre diJsonDocument
. Per creare una copia, chiamare JsonElement.Clone. Per ottenere ulteriori informazioni, consultare la sezione JsonDocument è IDisposable.
Il codice di esempio precedente conta gli studenti incrementando una variabile count
con ogni iterazione. Un'alternativa consiste nel chiamare GetArrayLength, come illustrato nell'esempio seguente:
double sum = 0;
int count = 0;
using (JsonDocument document = JsonDocument.Parse(jsonString))
{
JsonElement root = document.RootElement;
JsonElement studentsElement = root.GetProperty("Students");
count = studentsElement.GetArrayLength();
foreach (JsonElement student in studentsElement.EnumerateArray())
{
if (student.TryGetProperty("Grade", out JsonElement gradeElement))
{
sum += gradeElement.GetDouble();
}
else
{
sum += 70;
}
}
}
double average = sum / count;
Console.WriteLine($"Average grade : {average}");
Dim sum As Double = 0
Dim count As Integer = 0
Using document As JsonDocument = JsonDocument.Parse(jsonString)
Dim root As JsonElement = document.RootElement
Dim studentsElement As JsonElement = root.GetProperty("Students")
count = studentsElement.GetArrayLength()
For Each student As JsonElement In studentsElement.EnumerateArray()
Dim gradeElement As JsonElement = Nothing
If student.TryGetProperty("Grade", gradeElement) Then
sum += gradeElement.GetDouble()
Else
sum += 70
End If
Next
End Using
Dim average As Double = sum / count
Console.WriteLine($"Average grade : {average}")
Di seguito è riportato un esempio di JSON elaborato da questo codice:
{
"Class Name": "Science",
"Teacher\u0027s Name": "Jane",
"Semester": "2019-01-01",
"Students": [
{
"Name": "John",
"Grade": 94.3
},
{
"Name": "James",
"Grade": 81.0
},
{
"Name": "Julia",
"Grade": 91.9
},
{
"Name": "Jessica",
"Grade": 72.4
},
{
"Name": "Johnathan"
}
],
"Final": true
}
Per un esempio simile che usa JsonNode
anziché JsonDocument
, consultare la sezione Esempio di voto medio JsonNode.
Come cercare sottoelementi in JsonDocument e JsonElement
Le ricerche in JsonElement
richiedono una ricerca sequenziale delle proprietà e quindi sono relativamente lente (ad esempio quando si usa TryGetProperty
). System.Text.Json è progettato per ridurre al minimo il tempo di analisi iniziale anziché il tempo di ricerca. Pertanto, usare gli approcci seguenti per ottimizzare le prestazioni durante la ricerca in un oggetto JsonDocument
:
- Usare gli enumeratori predefiniti (EnumerateArray e EnumerateObject) anziché eseguire cicli o indicizzazione personalizzati.
- Non eseguire una ricerca sequenziale nell'intero
JsonDocument
per ogni proprietà usandoRootElement
. Cercare invece gli oggetti JSON annidati in base alla struttura nota dei dati JSON. Gli esempi di codice precedenti, ad esempio, cercano una proprietàGrade
negli oggettiStudent
eseguendo un ciclo negli oggettiStudent
e recuperando il valore diGrade
per ognuno, anziché eseguire ricerche in tutti gli oggettiJsonElement
alla ricerca delle proprietàGrade
. Quest'ultima operazione comporterebbe passaggi non necessari sugli stessi dati.
Confrontare JsonElements
Per confrontare due JsonElement
oggetti per verificarne l'uguaglianza, inclusi i relativi elementi discendenti, usare il JsonElement.DeepEquals(JsonElement, JsonElement) metodo .
JsonElement left = JsonDocument.Parse("10e-3").RootElement;
JsonElement right = JsonDocument.Parse("0.01").RootElement;
bool equal = JsonElement.DeepEquals(left, right);
Console.WriteLine(equal); // True.
Usare JsonDocument
per scrivere JSON
L'esempio seguente illustra come scrivere JSON da un JsonDocument:
string jsonString = File.ReadAllText(inputFileName);
var writerOptions = new JsonWriterOptions
{
Indented = true
};
var documentOptions = new JsonDocumentOptions
{
CommentHandling = JsonCommentHandling.Skip
};
using FileStream fs = File.Create(outputFileName);
using var writer = new Utf8JsonWriter(fs, options: writerOptions);
using JsonDocument document = JsonDocument.Parse(jsonString, documentOptions);
JsonElement root = document.RootElement;
if (root.ValueKind == JsonValueKind.Object)
{
writer.WriteStartObject();
}
else
{
return;
}
foreach (JsonProperty property in root.EnumerateObject())
{
property.WriteTo(writer);
}
writer.WriteEndObject();
writer.Flush();
Dim jsonString As String = File.ReadAllText(inputFileName)
Dim writerOptions As JsonWriterOptions = New JsonWriterOptions With {
.Indented = True
}
Dim documentOptions As JsonDocumentOptions = New JsonDocumentOptions With {
.CommentHandling = JsonCommentHandling.Skip
}
Dim fs As FileStream = File.Create(outputFileName)
Dim writer As Utf8JsonWriter = New Utf8JsonWriter(fs, options:=writerOptions)
Dim document As JsonDocument = JsonDocument.Parse(jsonString, documentOptions)
Dim root As JsonElement = document.RootElement
If root.ValueKind = JsonValueKind.[Object] Then
writer.WriteStartObject()
Else
Return
End If
For Each [property] As JsonProperty In root.EnumerateObject()
[property].WriteTo(writer)
Next
writer.WriteEndObject()
writer.Flush()
Il codice precedente:
- Legge un file JSON, carica i dati in un
JsonDocument
e scrive codice JSON formattato (pretty-print) in un file. - Usa JsonDocumentOptions per specificare che i commenti nel codice JSON di input sono consentiti ma vengono ignorati.
- Al termine, chiama Flush per il writer. Un'alternativa consiste nel consentire lo scaricamento automatico del writer quando viene eliminato.
Di seguito è riportato un esempio di input JSON da elaborare con il codice di esempio:
{"Class Name": "Science","Teacher's Name": "Jane","Semester": "2019-01-01","Students": [{"Name": "John","Grade": 94.3},{"Name": "James","Grade": 81.0},{"Name": "Julia","Grade": 91.9},{"Name": "Jessica","Grade": 72.4},{"Name": "Johnathan"}],"Final": true}
Il risultato è il seguente output JSON formattato:
{
"Class Name": "Science",
"Teacher\u0027s Name": "Jane",
"Semester": "2019-01-01",
"Students": [
{
"Name": "John",
"Grade": 94.3
},
{
"Name": "James",
"Grade": 81.0
},
{
"Name": "Julia",
"Grade": 91.9
},
{
"Name": "Jessica",
"Grade": 72.4
},
{
"Name": "Johnathan"
}
],
"Final": true
}
JsonDocument è IDisposable
JsonDocument
crea una vista in memoria dei dati in un buffer in pool. Di conseguenza, il tipo JsonDocument
implementa IDisposable
e deve essere usato all'interno di un blocco using
.
Restituire un JsonDocument
dall'API solo se si vuole trasferire la proprietà della durata e la responsabilità dell'eliminazione al chiamante. Nella maggior parte degli scenari, ciò non è necessario. Se il chiamante deve lavorare con l'intero documento JSON, restituire il Clone di RootElement, ovvero JsonElement. Se il chiamante deve lavorare con un particolare elemento all'interno del documento JSON, restituire il Clone di tale JsonElement. Se si restituisce RootElement
o un sottoelemento direttamente senza creare un Clone
, il chiamante non sarà in grado di accedere al JsonElement
restituito dopo che il JsonDocument
che lo possiede è stato eliminato.
Di seguito è riportato un esempio che richiede di creare un Clone
:
public JsonElement LookAndLoad(JsonElement source)
{
string json = File.ReadAllText(source.GetProperty("fileName").GetString());
using (JsonDocument doc = JsonDocument.Parse(json))
{
return doc.RootElement.Clone();
}
}
Il codice precedente si aspetta un JsonElement
che contiene una proprietà fileName
. Apre il file JSON e crea un JsonDocument
. Il metodo presuppone che il chiamante voglia lavorare con l'intero documento, quindi restituisce il Clone
di RootElement
.
Se si riceve un JsonElement
e si restituisce un sottoelemento, non è necessario restituire un Clone
del sottoelemento. Il chiamante è responsabile di mantenere vivo il JsonDocument
a cui appartiene il JsonElement
passato. Ad esempio:
public JsonElement ReturnFileName(JsonElement source)
{
return source.GetProperty("fileName");
}
JsonDocument
con JsonSerializerOptions
È possibile usare JsonSerializer
per serializzare e deserializzare un'istanza di JsonDocument
. Tuttavia, l'implementazione per la lettura e la scrittura di istanze di JsonDocument
tramite JsonSerializer
è un wrapper su JsonDocument.ParseValue(Utf8JsonReader) e JsonDocument.WriteTo(Utf8JsonWriter). Questo wrapper non inoltra alcun JsonSerializerOptions
(funzionalità del serializzatore) a Utf8JsonReader
o Utf8JsonWriter
. Ad esempio, se si imposta JsonSerializerOptions.DefaultIgnoreCondition su WhenWritingNull e si chiama JsonSerializer
con un overload che accetta JsonSerializerOptions
, le proprietà Null non verranno ignorate.
Nell'esempio seguente viene illustrato il risultato dell'utilizzo di metodi che accettano un parametro JsonSerializerOptions
e serializzano un'istanza di JsonDocument
:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace JsonDocumentWithJsonSerializerOptions;
public class Program
{
public static void Main()
{
Person person = new() { Name = "Nancy" };
// Default serialization - Address property included with null token.
// Output: {"Name":"Nancy","Address":null}
string personJsonWithNull = JsonSerializer.Serialize(person);
Console.WriteLine(personJsonWithNull);
// Serialize and ignore null properties - null Address property is omitted
// Output: {"Name":"Nancy"}
JsonSerializerOptions options = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
string personJsonWithoutNull = JsonSerializer.Serialize(person, options);
Console.WriteLine(personJsonWithoutNull);
// Ignore null properties doesn't work when serializing JsonDocument instance
// by using JsonSerializer.
// Output: {"Name":"Nancy","Address":null}
JsonDocument? personJsonDocument = JsonSerializer.Deserialize<JsonDocument>(personJsonWithNull);
personJsonWithNull = JsonSerializer.Serialize(personJsonDocument, options);
Console.WriteLine(personJsonWithNull);
}
}
public class Person
{
public string? Name { get; set; }
public string? Address { get; set; }
}
Se sono necessarie funzionalità JsonSerializerOptions
, usare JsonSerializer
con destinazioni fortemente tipizzate (come la classe Person
in questo esempio), anziché JsonDocument
.