【C#】ListをJSONへ高速変換する方法:System.Text.JsonとNewtonsoft.Jsonの比較
C#でListをJSONへ変換するなら標準のJsonSerializer.Serialize
が最速かつ追加依存なしで済みます。
JsonSerializerOptions
を添えればインデント整形やUnicodeエスケープ抑制も簡単に行え、日本語データも安全です。
旧来プロジェクトや細かな属性制御が必要な場面ではJsonConvert.SerializeObject
が依然有力な選択肢となります。
基本的な変換フロー
C#でList<T>
をJSON形式に変換する際、代表的な方法として標準ライブラリのSystem.Text.Json
と外部ライブラリのNewtonsoft.Json
があります。
ここでは、それぞれのライブラリを使った基本的なシリアライズ手順を具体的なコード例とともに解説します。
System.Text.Jsonによるシリアライズ
List<string>の最短手順
System.Text.Json
は.NET Core 3.0以降で標準搭載されている高速なJSONシリアライズライブラリです。
List<string>
のような単純なリストをJSON文字列に変換するのは非常に簡単です。
以下のサンプルコードは、文字列のリストをJSONに変換し、コンソールに出力する最短の手順を示しています。
using System;
using System.Collections.Generic;
using System.Text.Json;
class Program
{
static void Main()
{
// フルーツ名のリストを作成
var fruits = new List<string> { "Apple", "Banana", "Cherry" };
// List<string>をJSON文字列に変換
string jsonString = JsonSerializer.Serialize(fruits);
// 結果を表示
Console.WriteLine(jsonString);
}
}
["Apple","Banana","Cherry"]
このように、JsonSerializer.Serialize
メソッドにList<string>
を渡すだけで、簡単にJSON配列形式の文字列が得られます。
特別な設定は不要で、デフォルトの動作で十分なケースが多いです。
エンコードを保持した日本語変換
日本語などの非ASCII文字を含む文字列をJSONに変換すると、デフォルトではUnicodeエスケープ(\uXXXX
形式)されてしまうことがあります。
これを防ぎ、元の日本語文字をそのままJSONに出力したい場合は、JsonSerializerOptions
のEncoder
プロパティを設定します。
以下のコードは、日本語のリストをエスケープせずにそのままJSONに変換し、さらに見やすいようにインデント付きで出力する例です。
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Encodings.Web;
using System.Text.Unicode;
class Program
{
static void Main()
{
// 日本語の果物名リスト
var fruits = new List<string> { "りんご", "バナナ", "さくらんぼ" };
// Unicode文字をエスケープせずに出力するオプションを設定
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All),
WriteIndented = true // インデント付きで見やすくする
};
// JSONに変換
string jsonString = JsonSerializer.Serialize(fruits, options);
// 結果を表示
Console.WriteLine(jsonString);
}
}
[
"りんご",
"バナナ",
"さくらんぼ"
]
このように、JavaScriptEncoder.Create(UnicodeRanges.All)
を指定することで、すべてのUnicode文字をエスケープせずにそのままJSONに含められます。
日本語を含むデータを扱う場合は、この設定を使うと可読性が向上します。
Newtonsoft.Jsonによるシリアライズ
List<string>の最短手順
Newtonsoft.Json
(別名Json.NET)は、C#で長く使われているJSONライブラリで、多機能かつ柔軟なシリアライズが可能です。
NuGetからインストールして利用します。

まずは、List<string>
をJSONに変換する最短のコード例です。
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
class Program
{
static void Main()
{
// フルーツ名のリストを作成
var fruits = new List<string> { "Apple", "Banana", "Cherry" };
// List<string>をJSON文字列に変換
string jsonString = JsonConvert.SerializeObject(fruits);
// 結果を表示
Console.WriteLine(jsonString);
}
}
["Apple","Banana","Cherry"]
JsonConvert.SerializeObject
にリストを渡すだけで、簡単にJSON配列形式の文字列が得られます。
Newtonsoft.Json
は多くのプロジェクトで使われており、互換性も高いです。
Formatting指定での整形出力
Newtonsoft.Json
では、SerializeObject
の第2引数にFormatting.Indented
を指定することで、インデント付きの整形されたJSONを出力できます。
日本語を含む場合も同様に使えます。
以下は日本語のリストをインデント付きでJSONに変換する例です。
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
class Program
{
static void Main()
{
// 日本語の果物名リスト
var fruits = new List<string> { "りんご", "バナナ", "さくらんぼ" };
// インデント付きでJSONに変換
string jsonString = JsonConvert.SerializeObject(fruits, Formatting.Indented);
// 結果を表示
Console.WriteLine(jsonString);
}
}
[
"りんご",
"バナナ",
"さくらんぼ"
]
Formatting.Indented
を指定するだけで、改行やスペースが挿入されて見やすいJSONになります。
Newtonsoft.Json
は日本語の文字列も特別な設定なしでそのまま出力されるため、エンコードの心配はほとんどありません。
以上が、System.Text.Json
とNewtonsoft.Json
を使ったList<T>
の基本的なJSON変換の流れです。
どちらも簡単に使えますが、用途や環境に応じて使い分けることがポイントです。
オプションによる挙動の調整
System.Text.JsonのJsonSerializerOptions
インデント設定
System.Text.Json
でJSONを見やすく整形するには、JsonSerializerOptions
のWriteIndented
プロパティをtrue
に設定します。
これにより、改行やスペースが挿入され、階層構造がわかりやすくなります。
using System;
using System.Collections.Generic;
using System.Text.Json;
class Program
{
static void Main()
{
var fruits = new List<string> { "Apple", "Banana", "Cherry" };
var options = new JsonSerializerOptions
{
WriteIndented = true // インデントを有効にする
};
string jsonString = JsonSerializer.Serialize(fruits, options);
Console.WriteLine(jsonString);
}
}
[
"Apple",
"Banana",
"Cherry"
]
この設定は、デバッグ時やログ出力でJSONの構造を確認したい場合に便利です。
逆に、ファイルサイズを小さくしたい場合はfalse
にしてコンパクトなJSONを生成します。
Unicodeエスケープ抑制
デフォルトでは、System.Text.Json
は非ASCII文字をUnicodeエスケープ(\uXXXX
形式)で出力します。
日本語などの文字をそのまま表示したい場合は、Encoder
プロパティにJavaScriptEncoder.Create(UnicodeRanges.All)
を指定します。
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Encodings.Web;
using System.Text.Unicode;
class Program
{
static void Main()
{
var fruits = new List<string> { "りんご", "バナナ", "さくらんぼ" };
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All), // Unicodeエスケープを抑制
WriteIndented = true
};
string jsonString = JsonSerializer.Serialize(fruits, options);
Console.WriteLine(jsonString);
}
}
[
"りんご",
"バナナ",
"さくらんぼ"
]
この設定を使うと、JSONの可読性が向上し、特に日本語を含むデータを扱う際に便利です。
Null値の制御
System.Text.Json
では、シリアライズ時にnull
のプロパティを出力するかどうかをDefaultIgnoreCondition
プロパティで制御できます。
JsonIgnoreCondition.WhenWritingNull
を指定すると、null
の値はJSONに含まれません。
using System;
using System.Text.Json;
class Person
{
public string Name { get; set; }
public string? Nickname { get; set; }
}
class Program
{
static void Main()
{
var person = new Person { Name = "山田太郎", Nickname = null };
var options = new JsonSerializerOptions
{
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
};
string jsonString = JsonSerializer.Serialize(person, options);
Console.WriteLine(jsonString);
}
}
{
"Name": "山田太郎"
}
Nickname
がnull
のため、JSONには含まれていません。
null
も含めたい場合は、このオプションを設定しなければnull
も出力されます。
Newtonsoft.Jsonの設定プロパティ
FormattingとNullValueHandling
Newtonsoft.Json
では、Formatting
列挙体を使ってインデントの有無を指定します。
Formatting.Indented
で整形出力、Formatting.None
でコンパクト出力です。
また、NullValueHandling
プロパティでnull
値の出力制御が可能です。
NullValueHandling.Ignore
を指定すると、null
のプロパティはJSONに含まれません。
using System;
using Newtonsoft.Json;
class Person
{
public string Name { get; set; }
public string Nickname { get; set; }
}
class Program
{
static void Main()
{
var person = new Person { Name = "山田太郎", Nickname = null };
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented, // インデント付き出力
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
NullValueHandling = NullValueHandling.Ignore // null値を無視
};
string jsonString = JsonConvert.SerializeObject(person, settings);
Console.WriteLine(jsonString);
}
}
{
"Name": "山田太郎"
}
Nickname
がnull
のため、JSONに含まれていません。
NullValueHandling.Include
を指定するとnull
も出力されます。
DefaultValueHandlingとDateFormatString
DefaultValueHandling
は、プロパティのデフォルト値を持つ場合にシリアライズするかどうかを制御します。
例えば、DefaultValueHandling.Ignore
を指定すると、デフォルト値のプロパティはJSONに含まれません。
DateFormatString
は、日付型のプロパティをシリアライズする際のフォーマットを指定できます。
using System;
using Newtonsoft.Json;
class Event
{
public string Title { get; set; }
public DateTime Date { get; set; }
public int Count { get; set; } = 0;
}
class Program
{
static void Main()
{
var evt = new Event
{
Title = "セミナー",
Date = new DateTime(2024, 6, 1),
Count = 0 // デフォルト値
};
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
DefaultValueHandling = DefaultValueHandling.Ignore, // デフォルト値は無視
DateFormatString = "yyyy-MM-dd" // 日付フォーマット指定
};
string jsonString = JsonConvert.SerializeObject(evt, settings);
Console.WriteLine(jsonString);
}
}
{
"Title": "セミナー",
"Date": "2024-06-01"
}
Count
はデフォルト値の0
なのでJSONに含まれていません。
DateFormatString
で日付がyyyy-MM-dd
形式で出力されています。
これらの設定は、JSONのサイズ削減やフォーマット統一に役立ちます。
ネストされた型を含むList<T>の扱い
クラスオブジェクトのシリアライズ
System.Text.Jsonでのネスト構造
System.Text.Json
は、List<T>
の中に複雑なクラスオブジェクトが含まれていても、問題なくシリアライズできます。
クラスのプロパティは自動的にJSONのネスト構造として表現されます。
以下は、Person
クラスを要素とするリストをJSONに変換する例です。
using System;
using System.Collections.Generic;
using System.Text.Json;
class Program
{
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Address HomeAddress { get; set; }
}
class Address
{
public string City { get; set; }
public string Street { get; set; }
}
static void Main()
{
var people = new List<Person>
{
new Person
{
Name = "山田太郎",
Age = 30,
HomeAddress = new Address { City = "東京", Street = "千代田区1-1" }
},
new Person
{
Name = "鈴木花子",
Age = 25,
HomeAddress = new Address { City = "大阪", Street = "北区2-2" }
}
};
var options = new JsonSerializerOptions
{
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true
};
string jsonString = JsonSerializer.Serialize(people, options);
Console.WriteLine(jsonString);
}
}
[
{
"Name": "山田太郎",
"Age": 30,
"HomeAddress": {
"City": "東京",
"Street": "千代田区1-1"
}
},
{
"Name": "鈴木花子",
"Age": 25,
"HomeAddress": {
"City": "大阪",
"Street": "北区2-2"
}
}
]
このように、Person
クラスの中にAddress
クラスがネストされていても、System.Text.Json
は自動的に階層構造をJSONに反映します。
特別な設定は不要で、標準的なPOCO(Plain Old CLR Object)であれば問題なくシリアライズできます。
Newtonsoft.Jsonでのネスト構造
Newtonsoft.Json
も同様に、ネストされたクラスオブジェクトを含むList<T>
を簡単にシリアライズできます。
JsonConvert.SerializeObject
にリストを渡すだけで、ネスト構造がJSONに反映されます。
以下は先ほどと同じ構造の例です。
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
class Program
{
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Address HomeAddress { get; set; }
}
class Address
{
public string City { get; set; }
public string Street { get; set; }
}
static void Main()
{
var people = new List<Person>
{
new Person
{
Name = "山田太郎",
Age = 30,
HomeAddress = new Address { City = "東京", Street = "千代田区1-1" }
},
new Person
{
Name = "鈴木花子",
Age = 25,
HomeAddress = new Address { City = "大阪", Street = "北区2-2" }
}
};
string jsonString = JsonConvert.SerializeObject(people, Formatting.Indented);
Console.WriteLine(jsonString);
}
}
[
{
"Name": "山田太郎",
"Age": 30,
"HomeAddress": {
"City": "東京",
"Street": "千代田区1-1"
}
},
{
"Name": "鈴木花子",
"Age": 25,
"HomeAddress": {
"City": "大阪",
"Street": "北区2-2"
}
}
]
Newtonsoft.Json
は多機能でカスタマイズ性も高いですが、基本的なネスト構造のシリアライズは非常にシンプルに行えます。
Formatting.Indented
を指定して見やすく整形しています。
Dictionary入りListの変換
List<T>
の要素にDictionary<TKey, TValue>
が含まれている場合も、両ライブラリで問題なくシリアライズできます。
Dictionary
はJSONのオブジェクトとして表現されます。
以下は、List<Dictionary<string, string>>
をJSONに変換する例です。
using System;
using System.Collections.Generic;
using System.Text.Json;
class Program
{
static void Main()
{
var listOfDicts = new List<Dictionary<string, string>>
{
new Dictionary<string, string>
{
{ "Name", "山田太郎" },
{ "City", "東京" }
},
new Dictionary<string, string>
{
{ "Name", "鈴木花子" },
{ "City", "大阪" }
}
};
var options = new JsonSerializerOptions
{
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true
};
string jsonString = JsonSerializer.Serialize(listOfDicts, options);
Console.WriteLine(jsonString);
}
}
[
{
"Name": "山田太郎",
"City": "東京"
},
{
"Name": "鈴木花子",
"City": "大阪"
}
]
同様に、Newtonsoft.Json
でも同じようにシリアライズできます。
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
class Program
{
static void Main()
{
var listOfDicts = new List<Dictionary<string, string>>
{
new Dictionary<string, string>
{
{ "Name", "山田太郎" },
{ "City", "東京" }
},
new Dictionary<string, string>
{
{ "Name", "鈴木花子" },
{ "City", "大阪" }
}
};
string jsonString = JsonConvert.SerializeObject(listOfDicts, Formatting.Indented);
Console.WriteLine(jsonString);
}
}
[
{
"Name": "山田太郎",
"City": "東京"
},
{
"Name": "鈴木花子",
"City": "大阪"
}
]
Dictionary
のキーがJSONのプロパティ名、値が対応する値として出力されるため、動的なキー・値の組み合わせを扱う場合に便利です。
List<T>
の中にDictionary
が混在していても、どちらのライブラリでもスムーズにJSON変換が可能です。
パフォーマンス検証ポイント
シリアライズ速度の測定観点
JSONシリアライズの速度は、アプリケーションのレスポンスや処理時間に大きく影響します。
速度を正確に測定するには、以下のポイントに注意します。
- 対象データのサイズと構造
小規模な単純リストと、大規模かつネストされた複雑なオブジェクトでは処理時間が大きく異なります。
実際の利用シナリオに近いデータを用意することが重要です。
- ウォームアップの実施
JITコンパイルや初期化処理の影響を避けるため、測定前に数回シリアライズを実行してウォームアップします。
- 繰り返し回数の設定
複数回のシリアライズを行い、平均時間を算出することで安定した結果を得られます。
- 計測方法
System.Diagnostics.Stopwatch
を使い、開始から終了までの経過時間を計測します。
高精度な計測が可能です。
- 同期・非同期の違い
非同期APIを使う場合は、async/await
のオーバーヘッドも考慮します。
以下は速度測定の簡単な例です。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json;
class Program
{
static void Main()
{
var list = new List<string>();
for (int i = 0; i < 10000; i++)
{
list.Add("Item " + i);
}
var options = new JsonSerializerOptions { WriteIndented = false };
// ウォームアップ
JsonSerializer.Serialize(list, options);
var sw = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
JsonSerializer.Serialize(list, options);
}
sw.Stop();
Console.WriteLine($"平均シリアライズ時間: {sw.ElapsedMilliseconds / 100.0} ms");
}
}
このように、実際のデータ量や繰り返し回数を調整して測定します。
バッファ割り当てとメモリ効率
シリアライズ処理はメモリの割り当て頻度やサイズにも影響を受けます。
特に大量データを扱う場合、バッファの使い方がパフォーマンスに直結します。
- System.Text.Jsonの特徴
System.Text.Json
は内部でUtf8JsonWriter
を使い、バッファを効率的に管理しています。
バッファサイズを指定できるため、大きなデータを一括で処理する際にメモリ割り当てを抑えられます。
- Newtonsoft.Jsonの特徴
Newtonsoft.Json
はStringWriter
やJsonTextWriter
を使い、文字列バッファを動的に拡張します。
大量データではバッファの再割り当てが多くなり、メモリ使用量が増えることがあります。
- バッファプールの活用
System.Text.Json
は.NETのArrayPool<byte>
を活用し、バッファの再利用を促進しています。
これによりGC負荷が軽減されます。
- メモリプロファイリング
実際のアプリケーションでは、メモリプロファイラーを使って割り当て状況を確認し、ボトルネックを特定します。
ファイルサイズの比較
生成されるJSONのファイルサイズも重要な指標です。
ファイルサイズが大きいと、ネットワーク転送やディスクI/Oに影響します。
- インデントの有無
インデント付き(Pretty Print)にすると可読性は上がりますが、スペースや改行が増えファイルサイズが大きくなります。
サイズを抑えたい場合はインデントを無効にします。
- Unicodeエスケープの影響
System.Text.Json
はデフォルトで非ASCII文字をUnicodeエスケープします。
これによりファイルサイズが増えることがあります。
エスケープを抑制するとサイズが小さくなる場合があります。
- デフォルト値やnullの除外
不要なnull
やデフォルト値を除外する設定を使うと、JSONのサイズを削減できます。
- 圧縮の検討
JSONファイルをgzipなどで圧縮することで、転送時のサイズを大幅に減らせます。
以下は、同じデータをSystem.Text.Json
とNewtonsoft.Json
でシリアライズし、インデントの有無でファイルサイズを比較した例です。
ライブラリ | インデント | ファイルサイズ(バイト) |
---|---|---|
System.Text.Json | なし | 12345 |
System.Text.Json | あり | 23456 |
Newtonsoft.Json | なし | 12500 |
Newtonsoft.Json | あり | 23600 |
(※数値は例示です。
実際のサイズはデータ内容や設定により異なります)
このように、用途に応じてインデントやエスケープ設定を調整し、パフォーマンスとファイルサイズのバランスを取ることが重要です。
エラーと注意事項
循環参照が存在するList
List<T>
の中に循環参照がある場合、シリアライズ時に無限ループや例外が発生することがあります。
例えば、オブジェクトAがオブジェクトBを参照し、オブジェクトBが再びオブジェクトAを参照している場合です。
System.Text.Jsonの場合
System.Text.Json
はデフォルトで循環参照を検出しません。
そのため、循環参照があるとJsonException
がスローされます。
これを回避するには、JsonSerializerOptions
のReferenceHandler
にReferenceHandler.Preserve
を設定します。
これにより、循環参照を特殊な$id
と$ref
で表現し、無限ループを防ぎます。
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
class Program
{
class Node
{
public string Name { get; set; }
public Node Next { get; set; }
}
static void Main()
{
var node1 = new Node { Name = "Node1" };
var node2 = new Node { Name = "Node2" };
node1.Next = node2;
node2.Next = node1; // 循環参照
var options = new JsonSerializerOptions
{
WriteIndented = true,
ReferenceHandler = ReferenceHandler.Preserve
};
string jsonString = JsonSerializer.Serialize(node1, options);
Console.WriteLine(jsonString);
}
}
{
"$id": "1",
"Name": "Node1",
"Next": {
"$id": "2",
"Name": "Node2",
"Next": {
"$ref": "1"
}
}
}
このように、循環参照を安全にシリアライズできます。
Newtonsoft.Jsonの場合
Newtonsoft.Json
はデフォルトで循環参照を検出し、例外をスローします。
回避するには、JsonSerializerSettings
のReferenceLoopHandling
をReferenceLoopHandling.Ignore
やReferenceLoopHandling.Serialize
に設定します。
using System;
using Newtonsoft.Json;
class Program
{
class Node
{
public string Name { get; set; }
public Node Next { get; set; }
}
static void Main()
{
var node1 = new Node { Name = "Node1" };
var node2 = new Node { Name = "Node2" };
node1.Next = node2;
node2.Next = node1; // 循環参照
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
PreserveReferencesHandling = PreserveReferencesHandling.Objects
};
string jsonString = JsonConvert.SerializeObject(node1, settings);
Console.WriteLine(jsonString);
}
}
{
"$id": "1",
"Name": "Node1",
"Next": {
"$id": "2",
"Name": "Node2",
"Next": {
"$ref": "1"
}
}
}
ReferenceLoopHandling.Ignore
を指定すると、循環参照のプロパティは無視されますが、データの一部が欠落する可能性があるため注意が必要です。
プロパティ名の大文字小文字差異
JSONのキー名は大文字小文字を区別しますが、C#のプロパティ名は慣習的に大文字で始まります。
シリアライズ・デシリアライズ時に大文字小文字の違いが問題になることがあります。
System.Text.Jsonの場合
デフォルトでは、System.Text.Json
はC#のプロパティ名をそのままJSONのキー名として使用します。
デシリアライズ時も大文字小文字を区別しますが、JsonSerializerOptions
のPropertyNameCaseInsensitive
をtrue
に設定すると、大文字小文字を無視してマッピングできます。
using System;
using System.Text.Json;
class Person
{
public string FirstName { get; set; }
}
class Program
{
static void Main()
{
string json = "{\"firstname\":\"Taro\"}";
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
var person = JsonSerializer.Deserialize<Person>(json, options);
Console.WriteLine(person.FirstName);
}
}
Taro
Newtonsoft.Jsonの場合
Newtonsoft.Json
はデフォルトで大文字小文字を区別しません。
JSONのキー名がfirstname
でも、C#のFirstName
プロパティにマッピングされます。
特別な設定は不要です。
非公開メンバーの取り扱い
シリアライズ対象のクラスに非公開private
やprotected
のメンバーがある場合、両ライブラリでの扱いが異なります。
System.Text.Jsonの場合
System.Text.Json
はデフォルトで公開されているプロパティのみをシリアライズ対象とします。
非公開メンバーは無視されます。
非公開メンバーをシリアライズしたい場合は、カスタムコンバーターを作成する必要があります。
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
class Person
{
public string Name { get; set; }
private int Age { get; set; } = 30;
public int GetAge() => Age;
}
class Program
{
static void Main()
{
var person = new Person { Name = "Taro" };
var options = new JsonSerializerOptions { WriteIndented = true };
string jsonString = JsonSerializer.Serialize(person, options);
Console.WriteLine(jsonString);
}
}
{
"Name": "Taro"
}
Age
は非公開なのでJSONに含まれていません。
Newtonsoft.Jsonの場合
Newtonsoft.Json
はデフォルトで公開プロパティのみをシリアライズしますが、JsonProperty
属性を使うか、DefaultContractResolver
をカスタマイズすることで非公開メンバーもシリアライズ可能です。
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
class Person
{
public string Name { get; set; }
[JsonProperty]
private int Age { get; set; } = 30;
public int GetAge() => Age;
}
class Program
{
static void Main()
{
var person = new Person { Name = "Taro" };
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented
};
string jsonString = JsonConvert.SerializeObject(person, settings);
Console.WriteLine(jsonString);
}
}
{
"Name": "Taro",
"Age": 30
}
[JsonProperty]
属性を付けることで、非公開のAge
プロパティもシリアライズされます。
カスタマイズ性が高いため、細かい制御が必要な場合に便利です。
属性による制御テクニック
JsonPropertyNameとJsonProperty
JSONのキー名をC#のプロパティ名と異なる名前にしたい場合、System.Text.Json
ではJsonPropertyName
属性を使い、Newtonsoft.Json
ではJsonProperty
属性を使います。
これにより、シリアライズ・デシリアライズ時のキー名を自由にカスタマイズできます。
System.Text.JsonのJsonPropertyName
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
class Person
{
[JsonPropertyName("first_name")]
public string FirstName { get; set; }
[JsonPropertyName("age_years")]
public int Age { get; set; }
}
class Program
{
static void Main()
{
var person = new Person { FirstName = "Taro", Age = 30 };
var options = new JsonSerializerOptions { WriteIndented = true };
string jsonString = JsonSerializer.Serialize(person, options);
Console.WriteLine(jsonString);
}
}
{
"first_name": "Taro",
"age_years": 30
}
このように、JsonPropertyName
属性でJSONのキー名を指定できます。
Newtonsoft.JsonのJsonProperty
using System;
using Newtonsoft.Json;
class Person
{
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("age_years")]
public int Age { get; set; }
}
class Program
{
static void Main()
{
var person = new Person { FirstName = "Taro", Age = 30 };
string jsonString = JsonConvert.SerializeObject(person, Formatting.Indented);
Console.WriteLine(jsonString);
}
}
{
"first_name": "Taro",
"age_years": 30
}
JsonProperty
属性を使うことで、同様にキー名をカスタマイズできます。
JsonIgnoreとJsonInclude
特定のプロパティをシリアライズ・デシリアライズから除外したい場合、System.Text.Json
ではJsonIgnore
属性を使います。
逆に、非公開メンバーを含めたい場合はJsonInclude
属性を使います。
Newtonsoft.Json
でもJsonIgnore
属性が利用可能です。
System.Text.JsonのJsonIgnore
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
class Person
{
public string Name { get; set; }
[JsonIgnore]
public int Age { get; set; }
}
class Program
{
static void Main()
{
var person = new Person { Name = "Taro", Age = 30 };
var options = new JsonSerializerOptions { WriteIndented = true };
string jsonString = JsonSerializer.Serialize(person, options);
Console.WriteLine(jsonString);
}
}
{
"Name": "Taro"
}
Age
プロパティはJsonIgnore
で除外され、JSONに含まれません。
System.Text.JsonのJsonInclude
非公開のフィールドやプロパティをシリアライズ対象に含めたい場合、JsonInclude
を使います。
これは主にprivate
やprotected
の読み取り専用プロパティに適用します。
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
class Person
{
public string Name { get; set; }
[JsonInclude]
public int Age { get; private set; }
public Person(int age)
{
Age = age;
}
}
class Program
{
static void Main()
{
var person = new Person(30) { Name = "Taro" };
var options = new JsonSerializerOptions { WriteIndented = true };
string jsonString = JsonSerializer.Serialize(person, options);
Console.WriteLine(jsonString);
}
}
{
"Name": "Taro",
"Age": 30
}
Newtonsoft.JsonのJsonIgnore
using System;
using Newtonsoft.Json;
class Person
{
public string Name { get; set; }
[JsonIgnore]
public int Age { get; set; }
}
class Program
{
static void Main()
{
var person = new Person { Name = "Taro", Age = 30 };
string jsonString = JsonConvert.SerializeObject(person, Formatting.Indented);
Console.WriteLine(jsonString);
}
}
{
"Name": "Taro"
}
Newtonsoft.Json
ではJsonInclude
に相当する属性はありませんが、非公開メンバーをシリアライズしたい場合はカスタム設定やJsonProperty
属性を使います。
JsonConverterのカスタム実装
標準のシリアライズ方法では対応できない特殊な型やフォーマットを扱う場合、カスタムコンバーターを実装して制御できます。
System.Text.Json
とNewtonsoft.Json
の両方でカスタムコンバーターを作成可能です。
System.Text.Jsonのカスタムコンバーター例
以下は、DateTime
をyyyyMMdd
形式の文字列でシリアライズ・デシリアライズするカスタムコンバーターの例です。
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
class DateOnlyConverter : JsonConverter<DateTime>
{
private const string Format = "yyyyMMdd";
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string s = reader.GetString();
return DateTime.ParseExact(s, Format, null);
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString(Format));
}
}
class Event
{
public string Name { get; set; }
[JsonConverter(typeof(DateOnlyConverter))]
public DateTime Date { get; set; }
}
class Program
{
static void Main()
{
var evt = new Event { Name = "セミナー", Date = new DateTime(2024, 6, 1) };
var options = new JsonSerializerOptions
{
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true
};
string jsonString = JsonSerializer.Serialize(evt, options);
Console.WriteLine(jsonString);
var deserialized = JsonSerializer.Deserialize<Event>(jsonString, options);
Console.WriteLine(deserialized.Date.ToString("yyyy-MM-dd"));
}
}
{
"Name": "セミナー",
"Date": "20240601"
}
2024-06-01
Newtonsoft.Jsonのカスタムコンバーター例
同様に、Newtonsoft.Json
でもカスタムコンバーターを作成できます。
using System;
using Newtonsoft.Json;
class DateOnlyConverter : JsonConverter
{
private const string Format = "yyyyMMdd";
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)
{
string s = (string)reader.Value;
return DateTime.ParseExact(s, Format, null);
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTime);
}
}
class Event
{
public string Name { get; set; }
[JsonConverter(typeof(DateOnlyConverter))]
public DateTime Date { get; set; }
}
class Program
{
static void Main()
{
var evt = new Event { Name = "セミナー", Date = new DateTime(2024, 6, 1) };
string jsonString = JsonConvert.SerializeObject(evt, Formatting.Indented);
Console.WriteLine(jsonString);
var deserialized = JsonConvert.DeserializeObject<Event>(jsonString);
Console.WriteLine(deserialized.Date.ToString("yyyy-MM-dd"));
}
}
{
"Name": "セミナー",
"Date": "20240601"
}
2024-06-01
カスタムコンバーターを使うことで、独自のフォーマットや特殊な型のシリアライズ・デシリアライズを柔軟に実装できます。
asyncシリアライズとストリーム出力
System.Text.Jsonの非同期API
System.Text.Json
は.NET Core 3.0以降で非同期のシリアライズ・デシリアライズAPIを提供しています。
これにより、大きなデータをファイルやネットワークストリームに対して効率的に処理できます。
非同期処理はUIの応答性を保ったり、I/O待ち時間を有効活用したい場合に有効です。
以下は、List<string>
を非同期にファイルへシリアライズする例です。
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
var fruits = new List<string> { "りんご", "バナナ", "さくらんぼ" };
// ファイルストリームを非同期で開く
await using var stream = new FileStream("fruits.json", FileMode.Create, FileAccess.Write, FileShare.None, 4096, useAsync: true);
var options = new JsonSerializerOptions
{
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(System.Text.Unicode.UnicodeRanges.All),
WriteIndented = true
};
// 非同期でシリアライズしてストリームに書き込む
await JsonSerializer.SerializeAsync(stream, fruits, options);
Console.WriteLine("非同期シリアライズが完了しました。");
}
}
このコードは、SerializeAsync
メソッドを使ってList<string>
をJSON形式でファイルに非同期書き込みしています。
FileStream
のuseAsync: true
指定により、I/O操作も非同期で行われます。
ファイルに書き込まれたfruits.json
の内容は以下のようになります。
[
"りんご",
"バナナ",
"さくらんぼ"
]
非同期APIはメモリ使用量を抑えつつ、I/O待ち時間を効率的に処理できるため、大量データのシリアライズに適しています。
Newtonsoft.Jsonの非同期API
Newtonsoft.Json
はバージョン12.0以降で非同期のシリアライズ・デシリアライズAPIをサポートしています。
JsonTextWriter
とStreamWriter
を組み合わせて非同期にJSONを書き込むことが可能です。
以下は、List<string>
を非同期にファイルへシリアライズする例です。
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
class Program
{
static async Task Main()
{
var fruits = new List<string> { "りんご", "バナナ", "さくらんぼ" };
await using var stream = new FileStream("fruits_newtonsoft.json", FileMode.Create, FileAccess.Write, FileShare.None, 4096, useAsync: true);
await using var streamWriter = new StreamWriter(stream);
using var jsonWriter = new JsonTextWriter(streamWriter) { Formatting = Formatting.Indented };
var serializer = new JsonSerializer();
// 非同期でシリアライズしてストリームに書き込む
await jsonWriter.WriteStartArrayAsync();
foreach (var fruit in fruits)
{
await jsonWriter.WriteValueAsync(fruit);
}
await jsonWriter.WriteEndArrayAsync();
await jsonWriter.FlushAsync();
Console.WriteLine("Newtonsoft.Jsonの非同期シリアライズが完了しました。");
}
}
この例では、JsonTextWriter
の非同期メソッドを使ってJSON配列をストリームに書き込んでいます。
JsonSerializer.SerializeAsync
のような単一メソッドは存在しないため、手動で配列の開始・終了を書き込み、要素を一つずつ非同期で書いています。
ファイルfruits_newtonsoft.json
の内容は以下の通りです。
[
"りんご",
"バナナ",
"さくらんぼ"
]
Newtonsoft.Json
の非同期APIは細かい制御が可能ですが、System.Text.Json
に比べてやや冗長になる点に注意が必要です。
大量データのストリーム処理や部分的な書き込みに向いています。
JsonNode/JsonDocumentとの連携
部分更新に向いたDiff方式
System.Text.Json
のJsonNode
やJsonDocument
は、JSONデータを動的に操作できるAPIとして便利です。
特に、既存のJSON構造の一部だけを更新したい場合に役立ちます。
これにより、全体を再シリアライズすることなく、差分(Diff)を適用して効率的に部分更新が可能です。
以下は、JsonNode
を使ってJSONの一部を変更する例です。
using System;
using System.Text.Json;
using System.Text.Json.Nodes;
class Program
{
static void Main()
{
string json = @"
{
""Name"": ""山田太郎"",
""Age"": 30,
""Address"": {
""City"": ""東京"",
""Street"": ""千代田区1-1""
}
}";
// JSON文字列をJsonNodeとして読み込む
JsonNode rootNode = JsonNode.Parse(json);
// 部分的に値を更新
rootNode["Age"] = 31;
rootNode["Address"]["Street"] = "千代田区2-2";
// 更新後のJSONを文字列化
string updatedJson = rootNode.ToJsonString(new JsonSerializerOptions
{
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true
});
Console.WriteLine(updatedJson);
}
}
{
"Name": "山田太郎",
"Age": 31,
"Address": {
"City": "東京",
"Street": "千代田区2-2"
}
}
このように、JsonNode
を使うと、JSONの特定のノードだけを直接操作でき、差分更新が簡単に行えます。
JsonDocument
は読み取り専用ですが、JsonNode
は書き込みも可能なため、部分更新に適しています。
Diff方式の利点は、巨大なJSONデータの一部だけを変更して再シリアライズのコストを抑えられることです。
APIや設定ファイルの一部だけを動的に書き換えたい場合などに有効です。
動的なフィルタリング処理
JsonNode
やJsonDocument
は、動的にJSONの内容を解析・抽出できるため、条件に応じたフィルタリング処理にも適しています。
例えば、JSON配列の中から特定の条件を満たす要素だけを抽出したり、不要な要素を除外したりすることが可能です。
以下は、JsonNode
を使ってJSON配列から特定の条件に合う要素だけを抽出する例です。
using System;
using System.Text.Json;
using System.Text.Json.Nodes;
class Program
{
static void Main()
{
// 元の JSON 文字列
string json = @"
[
{ ""Name"": ""山田太郎"", ""Age"": 30 },
{ ""Name"": ""鈴木花子"", ""Age"": 25 },
{ ""Name"": ""佐藤次郎"", ""Age"": 40 }
]";
// JSON 配列を JsonNode としてパースし、JsonArray を取得
JsonArray jsonArray = JsonNode.Parse(json).AsArray();
// 年齢が 30 以上の要素だけを格納するための新しい JsonArray
var filtered = new JsonArray();
foreach (var item in jsonArray)
{
// 各要素の "Age" を取り出して比較
int age = item["Age"].GetValue<int>();
if (age >= 30)
{
// クローンを作成してから追加
filtered.Add(item.DeepClone());
}
}
// フィルタリング結果を見やすくインデント付きでシリアル化
string filteredJson = filtered.ToJsonString(new JsonSerializerOptions
{
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true
});
Console.WriteLine(filteredJson);
}
}
[
{
"Name": "山田太郎",
"Age": 30
},
{
"Name": "佐藤次郎",
"Age": 40
}
]
このように、JsonNode
を使うと動的にJSONの内容を操作し、条件に応じたフィルタリングや加工が簡単に行えます。
Newtonsoft.Json
のJObject
やJArray
に似た使い勝手で、柔軟なJSON操作が可能です。
動的なフィルタリングは、APIレスポンスの一部だけを抽出したい場合や、設定ファイルの特定項目だけを取得・変更したい場合に役立ちます。
JSONの構造が固定されていないケースでも柔軟に対応できます。
まとめ
この記事では、C#でのList<T>
をJSONに高速かつ柔軟に変換する方法を、System.Text.Json
とNewtonsoft.Json
の両ライブラリを中心に解説しました。
基本的なシリアライズ手順から、オプション設定による挙動調整、ネスト構造や辞書型の扱い、パフォーマンスのポイント、エラー対策、属性による制御、非同期処理、さらにJsonNode
やJsonDocument
を活用した部分更新や動的フィルタリングまで幅広く理解できます。
用途や要件に応じて最適な手法を選択し、効率的なJSON処理を実現してください。