System.Text.Json で JSON ドキュメント オブジェクト モデルを使用する方法
この記事では、JSON ドキュメント オブジェクト モデル (DOM) を使って、JSON ペイロードのデータにランダム アクセスする方法について説明します。
JSON DOM の選択肢
次の場合、DOM を使うことは、JsonSerializer による逆シリアル化の代替手段です。
- 逆シリアル化する型がない。
- 受信した JSON に固定スキーマがなく、含まれている内容を確認するために検査する必要がある。
System.Text.Json
には、JSON DOM を構築する 2 つの方法が用意されています。
JsonDocument を使用すると、
Utf8JsonReader
を使用して読み取り専用 DOM を構築することができます。 ペイロードを構成する JSON 要素には、JsonElement 型を使用してアクセスできます。JsonElement
型では、配列とオブジェクト列挙子と共に、JSON テキストを一般的な .NET 型に変換する API が提供されます。JsonDocument
では RootElement プロパティが公開されます。 詳細については、この記事で後述する「JsonDocument の使用」を参照してください。System.Text.Json.Nodes 名前空間の JsonNode およびその派生クラスを使用すると、変更可能な DOM を作成することができます。 ペイロードを構成する JSON 要素には、JsonNode、JsonObject、JsonArray、JsonValue、JsonElement 型を使用してアクセスできます。 詳細については、この記事で後述する「
JsonNode
の使用」を参照してください。
JsonDocument
と JsonNode
のどちらを使用するか選ぶときは、次の要素を考慮してください。
JsonNode
DOM は作成後に変更できます。JsonDocument
DOM は変更できません。JsonDocument
DOM では、そのデータにより高速にアクセスできます。
JsonNode
を使用します
以下の例では、System.Text.Json.Nodes 名前空間の JsonNode と他の型を使用して、次の操作を行う方法を示します。
- JSON 文字列から DOM を作成する
- DOM から JSON を書き込む。
- 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"
}
}
オブジェクト初期化子を使用して JsonNode DOM を作成し、変更を行う
以下の例では、次のことを行っています。
- オブジェクト初期化子を使用して DOM を作成する。
- 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
// }
// }
//}
}
}
JSON ペイロードのサブセクションを逆シリアル化する
次の例では、JsonNode を使用して、JSON ツリーのサブセクションに移動し、そのサブセクションからの単一の値、カスタム型、または配列を逆シリアル化する方法を示しています。
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
}
}
JsonNode の平均グレードの例
次の例では、整数値を持つ JSON 配列を選択し、平均値を計算します。
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
上記のコードでは次の操作が行われます。
Grade
プロパティを持つStudents
配列内のオブジェクトの平均グレードを計算します。- グレードのない学生には既定のグレード 70 を割り当てます。
JsonArray
のCount
プロパティから学生の数を取得します。
JsonSerializerOptions
を含む JsonNode
JsonSerializer
を使用すると、JsonNode
のインスタンスをシリアル化したり、逆シリアル化したりすることができます。 ただし、JsonSerializerOptions
を取るオーバーロードを使用する場合、オプション インスタンスはカスタム コンバーターを取得するためにのみ使用されます。 オプション インスタンスのその他の機能は使用されていません。 たとえば、JsonSerializerOptions.DefaultIgnoreCondition を WhenWritingNull に設定し、JsonSerializerOptions
を取るオーバーロードを使用して JsonSerializer
を呼び出した場合、null のプロパティは無視されません。
同じ制限が、JsonSerializerOptions
パラメーターとして WriteTo(Utf8JsonWriter, JsonSerializerOptions) や ToJsonString(JsonSerializerOptions) を取る JsonNode
メソッドに適用されます。 これらの API では、カスタム コンバーターを取得するためにのみ JsonSerializerOptions
を使用します。
次の例は、JsonSerializerOptions
パラメーターを取り、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; }
}
カスタム コンバーター以外の JsonSerializerOptions
の機能が必要な場合は、JsonNode
ではなく、厳密に型指定されたターゲット (この例の Person
クラスなど) を指定して JsonSerializer
を使用します。
プロパティの順序を操作する
JsonObject は、JsonNode のペイロード内の要素の 1 つであり、変更可能な JSON オブジェクトを表します。 この型は IDictionary<string, JsonNode>
としてモデル化されており、各エントリはオブジェクトのプロパティであり、暗黙的なプロパティの順序がカプセル化されています。 ただし、Insert(Int32, String, JsonNode) や RemoveAt(Int32) などの API では、特定のインデックスでの項目の挿入と削除を可能にすることで、この型を実質的に順序付きディクショナリとしてモデル化しています。 これらの API を使用すると、オブジェクト インスタンスを変更して、プロパティの順序に直接影響を与えることができます。
次のコードは、特定のプロパティをオブジェクトの先頭に追加または移動する例を示しています。
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;
}
JsonNode を比較する
2 つの JsonNode
オブジェクトを比較し、その子要素も含めて等しいかどうかを確認するには、JsonNode.DeepEquals(JsonNode, JsonNode) メソッドを使用します。
JsonDocument
を使用します
次の例は、JsonDocument クラスを使用して 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}")
上記のコードでは次の操作が行われます。
- 分析する JSON が
jsonString
という名前の文字列に含まれていると想定します。 Grade
プロパティを持つStudents
配列内のオブジェクトの平均グレードを計算します。- グレードのない学生には既定のグレード 70 を割り当てます。
JsonDocument
によってIDisposable
が実装されるため、using
ステートメントでJsonDocument
インスタンスを作成します。JsonDocument
インスタンスが破棄されると、そのすべてのJsonElement
インスタンスにもアクセスできなくなります。JsonElement
インスタンスへのアクセスを保持するには、親JsonDocument
インスタンスが破棄される前に、そのコピーを作成します。 コピーを作成するには、JsonElement.Clone を呼び出します。 詳細については、「JsonDocument は IDisposable」を参照してください。
前のコード例では、反復処理ごとに count
変数をインクリメントして学生をカウントします。 別の方法として、次の例に示すように GetArrayLength を呼び出すこともできます。
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}")
このコードで処理される JSON の例を次に示します。
{
"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
の代わりに JsonNode
を使用する同様の例については、「JsonNode の平均グレードの例」を参照してください。
JsonDocument と JsonElement でのサブ要素の検索方法
JsonElement
での検索にはプロパティの順次検索が必要になるため、比較的低速になります (たとえば、TryGetProperty
を使用する場合)。 System.Text.Json は、検索時間ではなく、初期解析時間を最小限に抑えるように設計されています。 そのため、JsonDocument
オブジェクトで検索する場合は、次の方法を使用してパフォーマンスを最適化してください。
- 独自のインデックス作成やループを実行するのではなく、組み込みの列挙子 (EnumerateArray と EnumerateObject) を使用します。
RootElement
を使用して、JsonDocument
全体ですべてのプロパティの順次検索を行わないでください。 代わりに、JSON データの既知の構造に基づいて、入れ子になった JSON オブジェクトで検索します。 たとえば上記のコード例では、Student
オブジェクトをループし、それぞれのGrade
の値を取得して、Student
オブジェクトでGrade
プロパティを探します。すべてのJsonElement
オブジェクトでGrade
プロパティを検索することは行いません。 後者を行うと、同じデータに対して不要なパスが行われます。
JsonElement を比較する
2 つの JsonElement
オブジェクトを比較し、その子要素も含めて等しいかどうかを確認するには、JsonElement.DeepEquals(JsonElement, JsonElement) メソッドを使用します。
JsonElement left = JsonDocument.Parse("10e-3").RootElement;
JsonElement right = JsonDocument.Parse("0.01").RootElement;
bool equal = JsonElement.DeepEquals(left, right);
Console.WriteLine(equal); // True.
JsonDocument
を使用して JSON を書き込む
次の例では、JsonDocument から JSON を書き込む方法を示します。
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()
上記のコードでは次の操作が行われます。
- JSON ファイルを読み取り、データを
JsonDocument
に読み込み、書式設定された (整形された) JSON をファイルに書き込みます。 - JsonDocumentOptions を使用して、入力 JSON 内でコメントは許可されるが無視されることを指定します。
- 完了したら、ライターに対して Flush を呼び出します。 別の方法として、破棄されたときにライターを自動フラッシュすることもできます。
コード例によって処理される JSON 入力の例を次に示します。
{"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}
その結果は、次のような整形された JSON 出力になります。
{
"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
では、データのメモリ内ビューがプールされたバッファー内に作成されます。 そのため、JsonDocument
型は IDisposable
を実装し、using
ブロック内で使用される必要があります。
有効期間中全体の所有権を呼び出し元に移譲し、責任を破棄する場合は、API から JsonDocument
のみを返します。 ほとんどのシナリオでは、これは必要ありません。 呼び出し元が JSON ドキュメント全体を操作する必要がある場合は、RootElement (つまり JsonElement) の Clone を返します。 呼び出し元が JSON ドキュメント内の特定の要素を操作する必要がある場合は、その JsonElement の Clone を返します。 Clone
を作成せずに直接 RootElement
またはサブ要素を返した場合、呼び出し元は、返された JsonElement
には、それを所有する JsonDocument
が破棄されるとアクセスできなくなります。
Clone
を作成する必要がある例を次に示します。
public JsonElement LookAndLoad(JsonElement source)
{
string json = File.ReadAllText(source.GetProperty("fileName").GetString());
using (JsonDocument doc = JsonDocument.Parse(json))
{
return doc.RootElement.Clone();
}
}
上記のコードでは、fileName
プロパティを含む JsonElement
が想定されています。 これにより、JSON ファイルが開き、JsonDocument
が作成されます。 このメソッドでは、呼び出し元がドキュメント全体を操作することが想定されているため、RootElement
の Clone
が返されます。
JsonElement
を受け取り、サブ要素を返す場合は、サブ要素の Clone
を返す必要はありません。 呼び出し元は、渡された JsonElement
が属している JsonDocument
が破棄されないように維持する役割を負っています。 次に例を示します。
public JsonElement ReturnFileName(JsonElement source)
{
return source.GetProperty("fileName");
}
JsonSerializerOptions
を含む JsonDocument
JsonSerializer
を使用すると、JsonDocument
のインスタンスをシリアル化したり、逆シリアル化したりすることができます。 ただし、JsonSerializer
を使用した JsonDocument
インスタンスの読み取りと書き込みの実装は、JsonDocument.ParseValue(Utf8JsonReader) と JsonDocument.WriteTo(Utf8JsonWriter) に対するラッパーです。 このラッパーは、どの JsonSerializerOptions
(シリアライザー機能) も Utf8JsonReader
や Utf8JsonWriter
に転送しません。 たとえば、JsonSerializerOptions.DefaultIgnoreCondition を WhenWritingNull に設定し、JsonSerializerOptions
を取るオーバーロードを使用して JsonSerializer
を呼び出した場合、null のプロパティは無視されません。
次の例は、JsonSerializerOptions
パラメーターを取り、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; }
}
JsonSerializerOptions
の機能が必要な場合は、JsonDocument
ではなく、厳密に型指定されたターゲット (この例の Person
クラスなど) を指定して JsonSerializer
を使用します。
こちらもご覧ください
.NET