【C#】System.Text.JsonとNewtonsoft.JsonでJSON作成・整形・カスタム変換を極める
C#でJSONを作成するなら標準のSystem.Text.Json
が高速で軽く、JsonSerializer.Serialize(object)
だけで手軽に文字列化でき、WriteIndented
で整形も可能です。
高度な変換や古い環境では機能が豊富なNewtonsoft.Json
が便利で、JsonConvert.SerializeObject
を使い同様に生成できます。
属性でキー名変更やカスタムコンバーターで独自フォーマットも扱え、目的と環境に応じ選択するのが得策です。
JSON作成のアプローチ概要
C#でJSONを作成する際には、主にSystem.Text.Json
とNewtonsoft.Json
(Json.NET)がよく使われています。
どちらのライブラリもJSONのシリアル化・逆シリアル化をサポートしていますが、プロジェクトの要件や環境によって適切な選択が必要です。
ここでは、JSON作成に使うライブラリを選ぶ際の重要なチェックポイントを解説いたします。
ライブラリ選択のチェックポイント
速度とメモリ消費
JSONのシリアル化や逆シリアル化は、アプリケーションのパフォーマンスに大きく影響します。
特に大量のデータを扱う場合やリアルタイム処理が求められる場合は、速度とメモリ消費のバランスが重要です。
- System.Text.Json
Microsoftが.NET Core 3.0以降で提供している標準ライブラリで、パフォーマンスに優れています。
内部的にSpan<T>やUtf8JsonReader/Writerを活用し、メモリ割り当てを最小限に抑えています。
例えば、JSONの読み書きにおいて高速で、GC(ガベージコレクション)発生を抑制する設計がなされています。
ただし、機能面でNewtonsoft.Jsonに比べてまだ一部不足している部分もあります。
- Newtonsoft.Json
長年にわたり広く使われてきたライブラリで、機能が非常に豊富です。
ただし、内部で文字列操作やリフレクションを多用するため、System.Text.Jsonに比べるとややメモリ消費が多く、処理速度も劣る傾向があります。
とはいえ、最適化やキャッシュを活用すれば十分なパフォーマンスを発揮します。
ライブラリ | シリアル化速度 | メモリ消費 | 備考 |
---|---|---|---|
System.Text.Json | 高速 | 低い | .NET標準、軽量設計 |
Newtonsoft.Json | 中程度 | やや多い | 機能豊富、互換性高 |
機能の充実度
JSONの作成や解析において、どのような機能が必要かも選定の重要なポイントです。
以下のような機能の有無を確認しましょう。
- 属性による細かい制御
どちらのライブラリも属性でプロパティ名の変更や無視設定が可能ですが、Newtonsoft.Jsonはより多彩な属性が用意されています。
例:JsonProperty
で名前変更、JsonIgnore
で除外、JsonConverter
でカスタム変換など。
- カスタムコンバーターの柔軟性
独自の型変換を行うカスタムコンバーターは両者でサポートされていますが、Newtonsoft.Jsonはより複雑なシナリオに対応しやすい設計です。
- 動的JSON操作
Newtonsoft.JsonはJObject
やJToken
を使った動的なJSON操作が可能で、構造が不明なJSONの処理に便利です。
System.Text.JsonもJsonDocument
やJsonElement
で読み取りはできますが、書き換えや動的操作は制限があります。
- シリアル化設定の柔軟性
例えば、循環参照の処理、Null値の扱い、日付フォーマットのカスタマイズなど、Newtonsoft.Jsonは細かい設定が可能です。
System.Text.Jsonも徐々に機能が拡充されていますが、まだNewtonsoft.Jsonに及ばない部分があります。
機能項目 | System.Text.Json | Newtonsoft.Json |
---|---|---|
属性によるプロパティ制御 | あり | あり |
カスタムコンバーター | あり | あり |
動的JSON操作 | 制限あり | 充実 |
循環参照対応 | 制限あり | あり |
日付・Enumフォーマット | 基本対応 | 豊富 |
依存関係とライセンス
ライブラリの依存関係やライセンスも選択時に考慮すべきポイントです。
- System.Text.Json
.NETの標準ライブラリとして組み込まれているため、追加の依存関係は不要です。
ライセンスは.NETのオープンソースライセンス(MITライセンス)で、商用利用も問題ありません。
- Newtonsoft.Json
NuGetパッケージとして提供されており、プロジェクトに追加する必要があります。
依存関係は少なく、ほぼ単独で動作します。
ライセンスはMITライセンスで、こちらも商用利用に制限はありません。
項目 | System.Text.Json | Newtonsoft.Json |
---|---|---|
依存関係 | .NET標準組み込み | NuGetパッケージ追加必要 |
ライセンス | MIT | MIT |
商用利用 | 問題なし | 問題なし |
以上のように、速度やメモリ消費、機能の充実度、依存関係やライセンスの観点からライブラリを選ぶことが重要です。
プロジェクトの規模や要件に応じて、最適なライブラリを選択してください。
System.Text.Jsonの使い方
基本シリアル化フロー
System.Text.Json
でJSONを作成する基本的な流れは、JsonSerializer.Serialize
メソッドを使ってC#のオブジェクトをJSON文字列に変換することです。
以下のサンプルコードは、シンプルなクラスPerson
のインスタンスをJSONにシリアル化しています。
using System;
using System.Text.Json;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public class Program
{
public static void Main()
{
var person = new Person
{
Name = "Alice",
Age = 28
};
string jsonString = JsonSerializer.Serialize(person);
Console.WriteLine(jsonString);
}
}
{"Name":"Alice","Age":28}
このように、Serialize
メソッドにオブジェクトを渡すだけでJSON文字列が生成されます。
逆に、JsonSerializer.Deserialize<T>
を使うとJSON文字列からオブジェクトに変換できます。
フォーマットオプション
WriteIndentedによる整形
デフォルトでは、System.Text.Json
はコンパクトなJSONを生成しますが、JsonSerializerOptions
のWriteIndented
プロパティをtrue
に設定すると、インデント付きの整形済みJSONを出力できます。
var options = new JsonSerializerOptions
{
WriteIndented = true
};
string jsonString = JsonSerializer.Serialize(person, options);
Console.WriteLine(jsonString);
{
"Name": "Alice",
"Age": 28
}
この設定は、ログ出力や人間が読みやすいJSONを生成したい場合に便利です。
Encoder設定と言語別エスケープ
JsonSerializerOptions
のEncoder
プロパティを使うと、JSON文字列内のエスケープ処理をカスタマイズできます。
例えば、HTMLタグや特定のUnicode文字をエスケープすることでXSS対策が可能です。
using System.Text.Encodings.Web;
using System.Text.Unicode;
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs),
WriteIndented = true
};
var data = new { Text = "<script>alert(\"XSS\")</script>あいう" };
string jsonString = JsonSerializer.Serialize(data, options);
Console.WriteLine(jsonString);
{
"Text": "\u003cscript\u003ealert(\"XSS\")\u003c/script\u003eあいう"
}
この例では、HTMLの<
や>
がUnicodeエスケープされており、CJK(漢字・ひらがな・カタカナ)はそのまま出力されています。
JsonSerializerOptionsの活用
IgnoreNullValuesとDefaultIgnoreCondition
IgnoreNullValues
は.NET 5.0まで使われていたプロパティで、null
のプロパティをJSONに含めない設定です。
現在はDefaultIgnoreCondition
に置き換えられています。
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
WriteIndented = true
};
var person = new Person
{
Name = "Bob",
Age = 0 // 0はnullではないため出力される
};
string jsonString = JsonSerializer.Serialize(person, options);
Console.WriteLine(jsonString);
{
"Name": "Bob",
"Age": 0
}
DefaultIgnoreCondition
はWhenWritingNull
のほかにNever
(常に書き込む)、WhenWritingDefault
(デフォルト値を無視)などが指定可能です。
PropertyNamingPolicyとDictionaryKeyPolicy
JSONのキー名を変換するために、PropertyNamingPolicy
とDictionaryKeyPolicy
を設定できます。
例えば、C#のPascalCaseのプロパティ名をcamelCaseに変換したい場合は以下のようにします。
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
var person = new Person
{
Name = "Charlie",
Age = 35
};
string jsonString = JsonSerializer.Serialize(person, options);
Console.WriteLine(jsonString);
{
"name": "Charlie",
"age": 35
}
PropertyNamingPolicy
はクラスのプロパティ名に、DictionaryKeyPolicy
は辞書のキーに適用されます。
属性ベースの設定
JsonPropertyNameでキー名変更
プロパティ名とJSONのキー名を個別に指定したい場合は、[JsonPropertyName]
属性を使います。
using System.Text.Json.Serialization;
public class Person
{
[JsonPropertyName("full_name")]
public string Name { get; set; }
[JsonPropertyName("years_old")]
public int Age { get; set; }
}
public class Program
{
public static void Main()
{
var person = new Person { Name = "Diana", Age = 40 };
var options = new JsonSerializerOptions { WriteIndented = true };
string jsonString = JsonSerializer.Serialize(person, options);
Console.WriteLine(jsonString);
}
}
{
"full_name": "Diana",
"years_old": 40
}
このように、JSONのキー名を自由にカスタマイズできます。
JsonIgnoreで出力除外
特定のプロパティをJSONに含めたくない場合は、[JsonIgnore]
属性を付けます。
public class Person
{
public string Name { get; set; }
[JsonIgnore]
public int Age { get; set; }
}
この場合、Age
はシリアル化されません。
JsonIncludeで非publicプロパティ公開
System.Text.Json
はデフォルトでpublicなプロパティのみシリアル化しますが、[JsonInclude]
属性を使うとprivateやinternalのプロパティやフィールドも含められます。
public class Person
{
[JsonInclude]
public int Id { get; private set; }
public string Name { get; set; }
public Person(int id)
{
Id = id;
}
}
この例では、Id
はprivate setterですがJSONに含まれます。
カスタムコンバーター作成手順
JsonConverter<T>の実装ポイント
独自の型や特殊な変換が必要な場合は、JsonConverter<T>
を継承してカスタムコンバーターを作成します。
以下はDateTime
を特定フォーマットでシリアル化・逆シリアル化する例です。
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Globalization;
public class DateTimeConverter : JsonConverter<DateTime>
{
private readonly string _format;
public DateTimeConverter(string format)
{
_format = format;
}
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string dateString = reader.GetString();
return DateTime.ParseExact(dateString, _format, CultureInfo.InvariantCulture);
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString(_format));
}
}
オプションへの登録方法
作成したコンバーターはJsonSerializerOptions.Converters
に追加して使います。
var options = new JsonSerializerOptions();
options.Converters.Add(new DateTimeConverter("yyyy-MM-dd"));
var obj = new { Date = new DateTime(2023, 6, 1) };
string jsonString = JsonSerializer.Serialize(obj, options);
Console.WriteLine(jsonString);
{"Date":"2023-06-01"}
Utf8JsonWriterの低レベル操作
バイナリSpanと組み合わせた高速書き込み
Utf8JsonWriter
は低レベルのJSON書き込みAPIで、バッファに直接書き込むため高速です。
Span<byte>
やMemory<byte>
と組み合わせて効率的にJSONを生成できます。
using System;
using System.Buffers;
using System.Text.Json;
public class Program
{
public static void Main()
{
var buffer = new ArrayBufferWriter<byte>();
using (var writer = new Utf8JsonWriter(buffer, new JsonWriterOptions { Indented = true }))
{
writer.WriteStartObject();
writer.WriteString("message", "Hello, Utf8JsonWriter!");
writer.WriteNumber("year", 2024);
writer.WriteEndObject();
writer.Flush();
}
string jsonString = System.Text.Encoding.UTF8.GetString(buffer.WrittenSpan);
Console.WriteLine(jsonString);
}
}
{
"message": "Hello, Utf8JsonWriter!",
"year": 2024
}
Flush制御とバッファ管理
Utf8JsonWriter
はFlush
メソッドでバッファの内容を確定させます。
大きなJSONを分割して書き込む場合やストリームに直接書き込む際に重要です。
バッファの再利用やArrayPool<byte>
と組み合わせることでメモリ効率を高められます。
JsonDocumentでの解析と再生成
LINQライクな要素走査
JsonDocument
は読み取り専用のJSON DOMで、JSON文字列を解析して要素を走査できます。
LINQのようにネストした要素を簡単に取得可能です。
using System;
using System.Text.Json;
public class Program
{
public static void Main()
{
string json = "{\"Name\":\"Eve\",\"Scores\":[100, 95, 88]}";
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
string name = root.GetProperty("Name").GetString();
var scores = root.GetProperty("Scores").EnumerateArray();
Console.WriteLine($"Name: {name}");
Console.Write("Scores: ");
foreach (var score in scores)
{
Console.Write($"{score.GetInt32()} ");
}
}
}
Name: Eve
Scores: 100 95 88
局所的な置換と部分更新
JsonDocument
は読み取り専用なので直接書き換えはできませんが、必要な部分だけを取り出して新しいJSONを作成することが可能です。
例えば、特定のプロパティを変更して再シリアル化する場合は、Utf8JsonWriter
などと組み合わせて処理します。
ソースジェネレーターによる高速化
AddJsonSerializableAttributeの使い方
.NET 6以降では、System.Text.Json
のソースジェネレーター機能を使い、コンパイル時にシリアル化コードを生成して高速化できます。
対象の型に[JsonSerializable(typeof(YourType))]
属性を付け、JsonSerializerContext
を継承したクラスを作成します。
using System.Text.Json.Serialization;
[JsonSerializable(typeof(Person))]
public partial class PersonJsonContext : JsonSerializerContext
{
}
シリアル化は以下のように行います。
string jsonString = JsonSerializer.Serialize(person, PersonJsonContext.Default.Person);
生成コードの確認とデバッグ
生成されたコードはビルド時に自動生成され、ILコードの最適化やリフレクションの回避により高速化されます。
Visual Studioの「オブジェクトブラウザー」や「デコンパイラー」で生成コードを確認可能です。
エラーと例外処理
JsonExceptionの主な発生パターン
System.Text.Json
のシリアル化・逆シリアル化で発生する例外は主にJsonException
です。
主な原因は以下の通りです。
- JSONの形式が不正(構文エラー)
- 型の不一致(例えば文字列を数値に変換しようとした)
- 必須プロパティの欠落
- カスタムコンバーター内の例外
読み取りオプションでの緩和策
JsonSerializerOptions
のAllowTrailingCommas
やReadCommentHandling
を設定すると、JSONの末尾のカンマやコメントを許容して柔軟に読み取れます。
var options = new JsonSerializerOptions
{
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip
};
string json = "{ \"Name\": \"Frank\", } // コメント";
var person = JsonSerializer.Deserialize<Person>(json, options);
これにより、多少フォーマットが厳密でないJSONでも例外を回避できます。
Newtonsoft.Jsonの使い方
基本シリアル化フロー
Newtonsoft.Json
(Json.NET)でJSONを作成する基本は、JsonConvert.SerializeObject
メソッドを使ってC#のオブジェクトをJSON文字列に変換することです。
以下はシンプルなPerson
クラスのインスタンスをシリアル化する例です。
using System;
using Newtonsoft.Json;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public class Program
{
public static void Main()
{
var person = new Person
{
Name = "Alice",
Age = 28
};
string jsonString = JsonConvert.SerializeObject(person);
Console.WriteLine(jsonString);
}
}
{"Name":"Alice","Age":28}
JsonConvert.DeserializeObject<T>
を使うと、JSON文字列からオブジェクトに変換できます。
FormattingとJsonSerializerSettings
JsonConvert.SerializeObject
は第2引数にFormatting
やJsonSerializerSettings
を指定して、出力の細かい制御が可能です。
NullValueHandling
NullValueHandling
は、null
のプロパティをJSONに含めるかどうかを制御します。
var person = new Person
{
Name = "Bob",
Age = 0,
// 例えばAddressがnullの場合
Address = null
};
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
Formatting = Formatting.Indented
};
string jsonString = JsonConvert.SerializeObject(person, settings);
Console.WriteLine(jsonString);
{
"Name": "Bob",
"Age": 0
}
Address
がnull
のためJSONに含まれていません。
DefaultValueHandling
DefaultValueHandling
は、プロパティのデフォルト値(例えばint
の0やbool
のfalse)をJSONに含めるか制御します。
var settings = new JsonSerializerSettings
{
DefaultValueHandling = DefaultValueHandling.Ignore,
Formatting = Formatting.Indented
};
var person = new Person
{
Name = "Carol",
Age = 0 // デフォルト値なので無視される
};
string jsonString = JsonConvert.SerializeObject(person, settings);
Console.WriteLine(jsonString);
{
"Name": "Carol"
}
Age
が0のため省略されています。
ReferenceLoopHandling
循環参照があるオブジェクトをシリアル化すると例外が発生します。
ReferenceLoopHandling
で回避方法を指定できます。
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Formatting = Formatting.Indented
};
var parent = new Person { Name = "Parent" };
var child = new Person { Name = "Child" };
parent.Child = child;
child.Parent = parent; // 循環参照
string jsonString = JsonConvert.SerializeObject(parent, settings);
Console.WriteLine(jsonString);
{
"Name": "Parent",
"Child": {
"Name": "Child"
}
}
Parent
のChild
のParent
プロパティは無視され、例外を回避しています。
属性ベースの設定
JsonPropertyでキー名変更
[JsonProperty]
属性を使うと、JSONのキー名を自由に変更できます。
using Newtonsoft.Json;
public class Person
{
[JsonProperty("full_name")]
public string Name { get; set; }
[JsonProperty("years_old")]
public int Age { get; set; }
}
シリアル化すると以下のようになります。
{
"full_name": "Dave",
"years_old": 45
}
JsonIgnoreで出力除外
[JsonIgnore]
属性を付けると、そのプロパティはシリアル化・逆シリアル化の対象から除外されます。
public class Person
{
public string Name { get; set; }
[JsonIgnore]
public int Age { get; set; }
}
Age
はJSONに含まれません。
JsonConverterAttributeで独自変換
特定のプロパティにカスタムコンバーターを適用したい場合は、[JsonConverter]
属性を使います。
public class Person
{
public string Name { get; set; }
[JsonConverter(typeof(CustomDateTimeConverter))]
public DateTime BirthDate { get; set; }
}
このように属性で指定すると、そのプロパティだけに独自の変換ロジックを適用できます。
カスタムコンバーター作成手順
JsonConverter継承とCanConvert実装
カスタムコンバーターはJsonConverter
を継承し、CanConvert
メソッドで対象型を指定します。
以下はDateTime
を特定フォーマットで処理する例です。
using Newtonsoft.Json;
using System;
public class CustomDateTimeConverter : JsonConverter
{
private readonly string _format = "yyyy-MM-dd";
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTime);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var date = (DateTime)value;
writer.WriteValue(date.ToString(_format));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var dateStr = (string)reader.Value;
return DateTime.ParseExact(dateStr, _format, null);
}
}
グローバル登録と局所登録の違い
- グローバル登録
JsonSerializerSettings.Converters
に追加すると、すべてのシリアル化・逆シリアル化で適用されます。
var settings = new JsonSerializerSettings();
settings.Converters.Add(new CustomDateTimeConverter());
- 局所登録
プロパティに[JsonConverter]
属性を付けると、そのプロパティだけに適用されます。
ContractResolverによる細粒度制御
DefaultContractResolver
デフォルトのDefaultContractResolver
は、プロパティ名をそのままJSONキーに使います。
カスタマイズのベースとして使われます。
CamelCasePropertyNamesContractResolver
CamelCasePropertyNamesContractResolver
を使うと、C#のPascalCaseのプロパティ名をcamelCaseに変換してJSONに出力します。
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Formatting = Formatting.Indented
};
var person = new Person { Name = "Ellen", Age = 30 };
string jsonString = JsonConvert.SerializeObject(person, settings);
Console.WriteLine(jsonString);
{
"name": "Ellen",
"age": 30
}
カスタムResolverの構築
DefaultContractResolver
を継承して、プロパティのシリアル化条件や名前変換を独自に制御できます。
using Newtonsoft.Json.Serialization;
public class CustomContractResolver : DefaultContractResolver
{
protected override string ResolvePropertyName(string propertyName)
{
return "prefix_" + propertyName.ToLower();
}
}
JToken/JObjectを用いた動的操作
要素の追加・削除・置換
JObject
は動的にJSONオブジェクトを操作できます。
以下は要素の追加例です。
using Newtonsoft.Json.Linq;
var obj = JObject.Parse("{\"Name\":\"Frank\"}");
obj["Age"] = 40; // 追加
obj.Remove("Name"); // 削除
obj["City"] = "Tokyo"; // 追加
Console.WriteLine(obj.ToString());
{
"Age": 40,
"City": "Tokyo"
}
JSONPathによるクエリ
SelectToken
メソッドでJSONPathを使い、特定の要素を抽出できます。
var json = @"{
'People': [
{ 'Name': 'Gina', 'Age': 25 },
{ 'Name': 'Hank', 'Age': 30 }
]
}";
var jObj = JObject.Parse(json);
var person = jObj.SelectToken("$.People[?(@.Name == 'Hank')]");
Console.WriteLine(person.ToString());
{
"Name": "Hank",
"Age": 30
}
日付・Enum・数値フォーマット
IsoDateTimeConverter
IsoDateTimeConverter
を使うと、ISO 8601形式で日付をシリアル化できます。
var settings = new JsonSerializerSettings
{
Converters = { new IsoDateTimeConverter() },
Formatting = Formatting.Indented
};
StringEnumConverter
StringEnumConverter
を使うと、Enum値を文字列としてJSONに出力できます。
using Newtonsoft.Json.Converters;
var settings = new JsonSerializerSettings
{
Converters = { new StringEnumConverter() },
Formatting = Formatting.Indented
};
CustomCreationConverterで複雑型生成
CustomCreationConverter<T>
を継承すると、逆シリアル化時に複雑な初期化処理を行えます。
エラーと例外処理
ErrorEventHandlerでの復旧
JsonSerializerSettings.Error
イベントにハンドラーを登録すると、シリアル化・逆シリアル化中のエラーを捕捉し、処理を継続できます。
var settings = new JsonSerializerSettings();
settings.Error += (sender, args) =>
{
Console.WriteLine($"Error: {args.ErrorContext.Error.Message}");
args.ErrorContext.Handled = true; // エラーを無視して続行
};
ErrorContextを利用したスキップ
ErrorContext
のHandled
プロパティをtrue
に設定すると、エラーが発生したプロパティをスキップして処理を続けられます。
TypeNameHandlingの注意点
型情報混入によるリスク
TypeNameHandling
を有効にすると、JSONに型情報$type
が埋め込まれます。
これにより、逆シリアル化時に任意の型を生成できるため、悪意あるJSONでコード実行のリスクが生じます。
安全な設定例
安全に使うには、SerializationBinder
を設定して許可する型を限定するか、TypeNameHandling
を必要最低限に抑えます。
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
SerializationBinder = new KnownTypesBinder()
};
KnownTypesBinder
は許可する型を明示的に管理するクラスです。
ライブラリ比較と選定ポイント
パフォーマンスベンチマーク
シリアル化速度の実測値
System.Text.Json
とNewtonsoft.Json
のシリアル化速度は、多くのベンチマークで比較されています。
一般的に、System.Text.Json
は.NET標準ライブラリとして最適化されており、特に.NET Core 3.0以降の環境で高速なシリアル化を実現しています。
例えば、同じ大きさのオブジェクトをシリアル化した場合、System.Text.Json
はNewtonsoft.Json
よりも約2倍程度高速に処理できるケースが多いです。
これは、System.Text.Json
がSpan<T>
やUtf8JsonWriter
などの低レベルAPIを活用し、メモリ割り当てを抑制しているためです。
ただし、複雑なカスタムコンバーターや動的なJSON操作を多用する場合は、Newtonsoft.Json
の方が最適化されているケースもあります。
シンプルなPOCOのシリアル化ではSystem.Text.Json
が優位ですが、機能の多様性が必要な場合は速度差が縮まることもあります。
メモリ割り当て量の比較
メモリ割り当ての観点でもSystem.Text.Json
は優れています。
Newtonsoft.Json
は内部で多くの文字列操作やリフレクションを行うため、ガベージコレクションの負荷が高くなる傾向があります。
一方、System.Text.Json
はバッファの再利用やSpan<T>
を活用し、不要なメモリ割り当てを減らしています。
これにより、大量のJSONデータを扱う場合やリアルタイム処理でGCの影響を抑えたい場合に有利です。
ライブラリ | シリアル化速度 | メモリ割り当て量 | 備考 |
---|---|---|---|
System.Text.Json | 高速 | 低い | .NET標準、低レベルAPI活用 |
Newtonsoft.Json | 中程度 | 多い | 機能豊富だが割り当て多め |
機能対比表
属性機能の網羅性
機能項目 | System.Text.Json | Newtonsoft.Json |
---|---|---|
プロパティ名変更属性 | [JsonPropertyName] | [JsonProperty] |
プロパティ無視属性 | [JsonIgnore] | [JsonIgnore] |
非publicプロパティのシリアル化 | [JsonInclude] | なし(カスタムで可能) |
カスタムコンバーター属性 | あり | あり |
Null値無視設定 | DefaultIgnoreCondition | NullValueHandling |
デフォルト値無視設定 | あり | DefaultValueHandling |
循環参照制御 | 制限あり | あり |
Newtonsoft.Json
は長年の実績から多彩な属性が揃っており、細かい制御が可能です。
System.Text.Json
は基本的な属性は揃っていますが、まだ一部機能が限定的です。
動的生成APIの有無
機能項目 | System.Text.Json | Newtonsoft.Json |
---|---|---|
動的JSON操作 | JsonDocument (読み取り専用) | JObject /JToken (読み書き可能) |
JSONPathサポート | なし | あり |
動的オブジェクト生成 | 制限あり | 充実 |
動的にJSONを操作したい場合は、Newtonsoft.Json
の方が柔軟で使いやすいです。
System.Text.Json
は読み取り専用のAPIが中心で、動的編集は難しいです。
移行と併用の戦略
NewtonsoftからSystem.Text.Jsonへの手順
- 依存関係の整理
既存プロジェクトからNewtonsoft.Json
の参照を確認し、System.Text.Json
を利用可能な環境(.NET Core 3.0以降)にアップデートします。
- シリアル化コードの置換
JsonConvert.SerializeObject
やDeserializeObject
をJsonSerializer.Serialize
、Deserialize
に置き換えます。
オプションや属性の違いに注意しながら、JsonSerializerOptions
を適切に設定します。
- カスタムコンバーターの移植
Newtonsoft.Json
のカスタムコンバーターはJsonConverter<T>
に書き換えが必要です。
APIの違いを理解して実装します。
- テストと検証
JSONの出力形式や動作が変わっていないか、単体テストや統合テストで確認します。
既存コードへの影響ポイント
- 属性の違い
Newtonsoft.Json
の[JsonProperty]
はSystem.Text.Json
の[JsonPropertyName]
に置き換えが必要です。
一部の属性は存在しないため、コード修正が必要です。
- 動的JSON操作の制限
JObject
やJToken
を多用している場合、System.Text.Json
では同様の操作が難しいため、コードの大幅な変更が必要です。
- 循環参照の扱い
System.Text.Json
は循環参照のサポートが限定的なので、設計見直しや回避策が必要になることがあります。
併用時の競合回避テクニック
- 名前空間の明示的指定
両ライブラリのクラス名が似ているため、using
ディレクティブで名前空間を明示的に指定し、混乱を避けます。
- 用途ごとに使い分け
例えば、APIのレスポンスはSystem.Text.Json
で処理し、動的JSON操作や複雑な変換はNewtonsoft.Json
で行うなど、役割を分けます。
- ラッパークラスの作成
JSON処理を抽象化したラッパークラスを作り、内部でどちらのライブラリを使うか切り替えられる設計にすると、将来的な移行が容易になります。
これらのポイントを踏まえ、プロジェクトの要件や既存コードの状況に応じて最適な選択と移行計画を立てることが重要です。
実践的シリアライズシナリオ
シンプルなPOCOのシリアライズ
POCO(Plain Old CLR Object)は、単純なクラスでプロパティのみを持つオブジェクトのことです。
System.Text.Json
やNewtonsoft.Json
を使って簡単にシリアライズできます。
using System;
using System.Text.Json;
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
public class Program
{
public static void Main()
{
var product = new Product
{
Name = "Laptop",
Price = 1299.99m
};
string json = JsonSerializer.Serialize(product, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);
}
}
{
"Name": "Laptop",
"Price": 1299.99
}
このように、単純なクラスはそのままシリアライズ可能で、WriteIndented
オプションで見やすく整形できます。
Dictionaryと匿名型の扱い
辞書型や匿名型もJSONにシリアライズできます。
辞書はキーと値のペアをJSONオブジェクトとして表現し、匿名型はプロパティ名をキーにしてシリアライズされます。
using System;
using System.Collections.Generic;
using System.Text.Json;
public class Program
{
public static void Main()
{
var dict = new Dictionary<string, int>
{
["apple"] = 3,
["banana"] = 5
};
string dictJson = JsonSerializer.Serialize(dict, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(dictJson);
var anon = new { City = "Tokyo", Population = 14000000 };
string anonJson = JsonSerializer.Serialize(anon, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(anonJson);
}
}
{
"apple": 3,
"banana": 5
}
{
"City": "Tokyo",
"Population": 14000000
}
辞書のキーは文字列である必要があり、匿名型は型名がないためシンプルにJSON化されます。
コレクションとネスト構造
リストや配列などのコレクションはJSONの配列としてシリアライズされます。
ネストしたオブジェクトもそのまま階層構造で表現されます。
using System;
using System.Collections.Generic;
using System.Text.Json;
public class Order
{
public int OrderId { get; set; }
public List<Product> Products { get; set; }
}
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
public class Program
{
public static void Main()
{
var order = new Order
{
OrderId = 1001,
Products = new List<Product>
{
new Product { Name = "Mouse", Price = 25.5m },
new Product { Name = "Keyboard", Price = 45.0m }
}
};
string json = JsonSerializer.Serialize(order, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);
}
}
{
"OrderId": 1001,
"Products": [
{
"Name": "Mouse",
"Price": 25.5
},
{
"Name": "Keyboard",
"Price": 45.0
}
]
}
ネスト構造はJSONの階層として自然に表現され、複雑なデータも扱いやすいです。
ストリームを使ったI/O
ファイル書き込み
JSONをファイルに書き込む場合は、FileStream
やStreamWriter
と組み合わせてシリアライズします。
using System;
using System.IO;
using System.Text.Json;
public class Program
{
public static void Main()
{
var data = new { Name = "FileTest", Value = 123 };
using (var stream = new FileStream("output.json", FileMode.Create))
{
JsonSerializer.Serialize(stream, data, new JsonSerializerOptions { WriteIndented = true });
}
Console.WriteLine("JSONファイルに書き込み完了");
}
}
このコードはoutput.json
ファイルに整形済みJSONを書き込みます。
ネットワーク送信
ネットワーク通信でJSONを送信する場合は、HttpClient
のStringContent
にJSON文字列をセットして送信します。
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
var client = new HttpClient();
var data = new { User = "network", Score = 99 };
string json = JsonSerializer.Serialize(data);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://example.com/api/score", content);
Console.WriteLine($"レスポンスステータス: {response.StatusCode}");
}
}
JSON文字列をHTTPリクエストのボディにセットして送信します。
JSONとWeb API連携
ASP.NET Core既定の動作
ASP.NET CoreはデフォルトでSystem.Text.Json
を使ってリクエストボディのJSONをモデルにバインドし、レスポンスをJSONで返します。
コントローラーのアクションでPOCOを返すだけで自動的にシリアライズされます。
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet("{id}")]
public ActionResult<Product> GetProduct(int id)
{
var product = new Product { Name = "Sample", Price = 100 };
return Ok(product);
}
}
この場合、Product
オブジェクトはJSONに変換されてクライアントに返されます。
カスタムフォーマッタ挿入
既定のJSONシリアライザーをカスタマイズしたい場合は、Startup.cs
のAddControllers
でJsonOptions
を設定します。
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.WriteIndented = true;
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
これにより、API全体のJSON出力がインデント付きかつcamelCaseになります。
設定ファイル活用
既存JSONのマージ更新
設定ファイルなど既存のJSONに新しい値を追加・更新したい場合は、一度JsonDocument
やJObject
で読み込み、必要な部分を変更してから再シリアライズします。
using System;
using System.IO;
using System.Text.Json;
public class Program
{
public static void Main()
{
string json = File.ReadAllText("config.json");
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement.Clone();
using var stream = new MemoryStream();
using (var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true }))
{
writer.WriteStartObject();
foreach (var property in root.EnumerateObject())
{
if (property.NameEquals("Setting1"))
{
writer.WriteString("Setting1", "UpdatedValue");
}
else
{
property.WriteTo(writer);
}
}
writer.WriteEndObject();
}
string updatedJson = System.Text.Encoding.UTF8.GetString(stream.ToArray());
File.WriteAllText("config.json", updatedJson);
Console.WriteLine("設定ファイルを更新しました");
}
}
コメント除去と再フォーマット
System.Text.Json
はコメントをサポートしませんが、JsonDocument
のJsonReaderOptions
でコメントをスキップして読み込み可能です。
読み込んだJSONを再フォーマットして保存することで、コメントを除去できます。
var options = new JsonDocumentOptions
{
CommentHandling = JsonCommentHandling.Skip
};
string jsonWithComments = @"
{
// コメント行
""Key"": ""Value""
}";
using var doc = JsonDocument.Parse(jsonWithComments, options);
string formattedJson = JsonSerializer.Serialize(doc.RootElement, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(formattedJson);
{
"Key": "Value"
}
コメントが除去され、整形されたJSONが得られます。
落とし穴と対策
循環参照の検出と回避
オブジェクト間で相互に参照し合う「循環参照」がある場合、JSONシリアライズ時に無限ループや例外が発生しやすいです。
特に親子関係や双方向のナビゲーションプロパティを持つクラスで注意が必要です。
- System.Text.Jsonの場合
デフォルトでは循環参照を検出するとJsonException
が発生します。
現状、循環参照を自動的に解決する機能は限定的で、ReferenceHandler.Preserve
を使うことで循環参照をサポートできますが、JSONに特殊な$id
や$ref
プロパティが挿入されるため、互換性に注意が必要です。
var options = new JsonSerializerOptions
{
ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.Preserve,
WriteIndented = true
};
- Newtonsoft.Jsonの場合
ReferenceLoopHandling
設定で循環参照を無視したり、エラーを回避できます。
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
- 回避策
- 循環参照のあるプロパティに
[JsonIgnore]
を付けて除外する - DTO(データ転送オブジェクト)を用いて循環参照を持たない構造に変換する
ReferenceHandler.Preserve
やReferenceLoopHandling.Ignore
を適切に設定する
- 循環参照のあるプロパティに
大文字小文字の取り扱い差異
JSONのキー名は大文字小文字を区別しますが、C#のプロパティ名はPascalCaseが一般的です。
System.Text.Json
とNewtonsoft.Json
ではデフォルトの大文字小文字の扱いに違いがあります。
- System.Text.Json
デフォルトでプロパティ名をそのまま出力し、大文字小文字を区別します。
逆シリアル化時も大文字小文字を厳密にマッチさせるため、JSONのキー名が異なるとマッピングされません。
- Newtonsoft.Json
デフォルトで大文字小文字を区別しません。
JSONのキー名がname
でもName
でもマッピング可能です。
- 対策
System.Text.Json
で大文字小文字を無視したい場合は、JsonSerializerOptions.PropertyNameCaseInsensitive
をtrue
に設定します
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
- JSONのキー名を属性や
PropertyNamingPolicy
で統一します
既定値の扱いで発生する抜け漏れ
シリアル化時にプロパティの既定値(例えばint
の0やbool
のfalse)が省略されることがあります。
これにより、JSONに期待したキーが含まれず、逆シリアル化時に値が欠落する問題が起こることがあります。
- System.Text.Json
DefaultIgnoreCondition
をWhenWritingDefault
に設定すると、既定値のプロパティは出力されません。
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault
};
- Newtonsoft.Json
DefaultValueHandling
をIgnore
に設定すると同様の動作になります。
- 対策
- 既定値を省略したくない場合は、これらの設定を
Never
やInclude
に変更します - 省略されることを前提に、逆シリアル化後のデフォルト値を考慮した設計にします
- 既定値を省略したくない場合は、これらの設定を
非同期処理とパフォーマンス低下
JSONのシリアル化・逆シリアル化はCPU負荷が高い処理であり、非同期メソッドを使っても必ずしもパフォーマンスが向上するとは限りません。
- System.Text.Json
JsonSerializer.SerializeAsync
やDeserializeAsync
はストリームを非同期に読み書きしますが、シリアル化自体は同期的に行われる部分もあります。
大量データのI/O待ちを減らせますが、CPU処理は非同期化されません。
- Newtonsoft.Json
非同期APIは限定的で、主に同期的な処理が中心です。
- パフォーマンス低下の原因
- 非同期処理のオーバーヘッド
- 大量データのシリアル化でCPU負荷が高い
- ストリームのバッファサイズが小さい場合の頻繁なI/O
- 対策
- 非同期APIはI/O待ちがボトルネックのときに使う
- バッファサイズを適切に設定し、I/O回数を減らす
- CPU負荷が高い場合はシリアル化処理を別スレッドやバッチ処理に分散する
これらの落とし穴を理解し、適切な設定や設計を行うことで、JSON処理の安定性とパフォーマンスを向上させられます。
セキュリティと安定性
特殊文字のエスケープ
JSON文字列内に含まれる特殊文字は適切にエスケープしないと、クロスサイトスクリプティング(XSS)などのセキュリティリスクを招く可能性があります。
特にWebアプリケーションでJSONをHTMLに埋め込む場合は注意が必要です。
XSS対策としてのEncoder設定
System.Text.Json
では、JsonSerializerOptions.Encoder
プロパティを設定することで、エスケープ処理をカスタマイズできます。
デフォルトではJavaScriptEncoder.Default
が使われ、基本的なエスケープは行われますが、HTMLタグや特定のUnicode文字を含む場合は追加のエスケープが必要です。
using System.Text.Encodings.Web;
using System.Text.Unicode;
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs),
WriteIndented = true
};
var data = new { Text = "<script>alert('XSS')</script>あいう" };
string jsonString = JsonSerializer.Serialize(data, options);
Console.WriteLine(jsonString);
{
"Text": "\u003cscript\u003ealert('XSS')\u003c/script\u003eあいう"
}
この例では、<
や>
などのHTMLタグに使われる文字がUnicodeエスケープされ、ブラウザでのスクリプト実行を防止します。
JavaScriptEncoder.Create
で許可するUnicode範囲を指定し、必要な文字だけをエスケープ対象から除外できます。
無効化時の注意点
エスケープ処理を無効化したり、カスタムエンコーダーでエスケープを緩めると、XSS攻撃のリスクが高まります。
特にユーザー入力を含むJSONをHTMLに埋め込む場合は、必ず適切なエスケープを行うことが重要です。
また、Newtonsoft.Json
ではStringEscapeHandling
プロパティでエスケープの強度を調整できますが、こちらも無効化は推奨されません。
型情報漏洩の防止
JSONに型情報を含める機能は便利ですが、悪意ある攻撃者に型情報を利用されると、リモートコード実行などの深刻な脆弱性につながる恐れがあります。
$typeプロパティの制御
Newtonsoft.Json
のTypeNameHandling
を有効にすると、JSONに$type
プロパティが埋め込まれ、逆シリアル化時に型を特定してインスタンス化します。
{
"$type": "Namespace.ClassName, AssemblyName",
"Property": "Value"
}
この機能は便利ですが、信頼できないJSONを処理すると、任意の型を生成されてしまうリスクがあります。
- 対策
TypeNameHandling
は必要最低限の範囲でのみ有効にしますSerializationBinder
(またはISerializationBinder
)を実装し、許可する型を限定します- 信頼できない入力に対しては
TypeNameHandling
を無効にします
System.Text.Json
はデフォルトで型情報を埋め込まないため、このリスクは低いですが、カスタム実装時は注意が必要です。
過剰データへの耐性
大量のデータや深いネスト構造のJSONを処理する際、メモリ不足やスタックオーバーフロー、DoS攻撃のリスクがあります。
これらを防ぐために、読み取り時の制限を設けることが重要です。
最大デプス設定
System.Text.Json
のJsonSerializerOptions.MaxDepth
で、JSONの最大ネスト深度を制限できます。
デフォルトは64ですが、必要に応じて調整可能です。
var options = new JsonSerializerOptions
{
MaxDepth = 32
};
深すぎるネストは例外JsonException
を発生させて処理を中断し、過剰なリソース消費を防ぎます。
Newtonsoft.Json
でもJsonSerializerSettings.MaxDepth
で同様の制限が可能です。
ストリーム読み取りの制限
ストリームからJSONを読み込む際は、読み取りサイズやタイムアウトを制御して、過剰なデータ受信を防ぎます。
例えば、StreamReader
のバッファサイズを適切に設定したり、CancellationToken
を使ってタイムアウトを設ける方法があります。
また、Web APIなどではリクエストボディの最大サイズをサーバー側で制限し、不正な大量データの送信を防止します。
これらのセキュリティと安定性のポイントを押さえ、JSON処理を安全かつ堅牢に実装することが重要です。
パフォーマンス向上のヒント
バッファ再利用とArrayPool
JSONのシリアル化や逆シリアル化では、一時的にバイト配列や文字配列のバッファを大量に確保することが多く、これがGC(ガベージコレクション)の負荷増大やパフォーマンス低下の原因になります。
ArrayPool<T>
を活用すると、バッファの再利用が可能になり、メモリ割り当てを大幅に削減できます。
System.Buffers.ArrayPool<T>
は、共有プールから配列を借りて使い終わったら返却する仕組みです。
これにより、頻繁な配列の新規確保を避けられます。
using System;
using System.Buffers;
using System.Text;
using System.Text.Json;
public class Program
{
public static void Main()
{
var pool = ArrayPool<byte>.Shared;
byte[] buffer = pool.Rent(1024); // 1024バイトのバッファを借りる
try
{
var writer = new Utf8JsonWriter(buffer.AsSpan());
writer.WriteStartObject();
writer.WriteString("message", "Hello ArrayPool!");
writer.WriteEndObject();
writer.Flush();
string json = Encoding.UTF8.GetString(buffer, 0, (int)writer.BytesCommitted);
Console.WriteLine(json);
}
finally
{
pool.Return(buffer); // バッファをプールに返却
}
}
}
{"message":"Hello ArrayPool!"}
このように、ArrayPool
を使うことでバッファの再利用ができ、GCの発生を抑えつつ高速なJSON書き込みが可能です。
特に大量のJSON処理や高頻度のシリアル化処理で効果が大きいです。
Span/Memoryの活用ポイント
Span<T>
とMemory<T>
は、.NETで導入されたメモリ効率の良いデータ操作のための構造体です。
これらを活用すると、配列や文字列のコピーを減らし、パフォーマンスを向上させられます。
Span<T>
スタック上に割り当てられ、範囲を指定して配列やメモリの一部を参照できます。
高速でGCの影響を受けませんが、非同期処理には使えません。
Memory<T>
ヒープ上に割り当てられ、非同期処理でも使えます。
Span<T>
に変換可能です。
System.Text.Json
のUtf8JsonWriter
やUtf8JsonReader
はSpan<byte>
やReadOnlySpan<byte>
を使って高速にバッファを操作しています。
using System;
using System.Text;
using System.Text.Json;
public class Program
{
public static void Main()
{
Span<byte> buffer = stackalloc byte[256]; // スタック上にバッファ確保
var writer = new Utf8JsonWriter(buffer);
writer.WriteStartObject();
writer.WriteString("status", "OK");
writer.WriteNumber("code", 200);
writer.WriteEndObject();
writer.Flush();
string json = Encoding.UTF8.GetString(buffer.Slice(0, (int)writer.BytesCommitted));
Console.WriteLine(json);
}
}
{"status":"OK","code":200}
この例では、スタック上のバッファを直接使うため、ヒープ割り当てが発生せず高速です。
Span
やMemory
を活用することで、余計なコピーや割り当てを減らし、効率的なJSON処理が可能になります。
シリアライザインスタンスのキャッシュ
System.Text.Json
のJsonSerializer
は静的メソッドで使うことが多いですが、JsonSerializerOptions
のインスタンスは使い回すことでパフォーマンスが向上します。
毎回新規にオプションを作成すると、内部での設定解析やキャッシュが無駄になります。
using System;
using System.Text.Json;
public class Program
{
private static readonly JsonSerializerOptions CachedOptions = new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
public static void Main()
{
var obj = new { Name = "Cache", Value = 123 };
// オプションを使い回すことで高速化
string json1 = JsonSerializer.Serialize(obj, CachedOptions);
string json2 = JsonSerializer.Serialize(obj, CachedOptions);
Console.WriteLine(json1);
Console.WriteLine(json2);
}
}
{
"name": "Cache",
"value": 123
}
{
"name": "Cache",
"value": 123
}
同じJsonSerializerOptions
を複数回使うことで、内部の型情報キャッシュやコンバーターの初期化コストを削減できます。
特に大量のシリアル化処理がある場合は、オプションのキャッシュが効果的です。
また、カスタムコンバーターを登録したオプションは使い回すことが推奨されます。
逆に毎回新規作成するとパフォーマンスが低下します。
これらのテクニックを組み合わせることで、C#でのJSONシリアル化・逆シリアル化のパフォーマンスを大幅に向上させられます。
まとめ
この記事では、C#でJSONを作成・整形・カスタム変換するための代表的なライブラリであるSystem.Text.Json
とNewtonsoft.Json
の使い方や特徴を詳しく解説しました。
基本的なシリアル化から属性設定、カスタムコンバーターの作成、パフォーマンス最適化、セキュリティ対策まで幅広くカバーしています。
用途や要件に応じて適切なライブラリを選び、効率的かつ安全にJSON処理を実装するための知識が身につきます。