C#: JSONとシリアライズ
1. JSON形式の基礎
JSON(JavaScript Object Notation)は、構造化されたデータをプレーンテキストとして保存・送信するための軽量なデータ交換形式です。
- オブジェクト:
{}で囲まれており、キーと値のペアを含みます。キーは文字列でなければなりません。 - 配列:
[]で囲まれた、値の順序付きリスト - データ型:文字列、数値、ブール値、null、オブジェクト、配列
{
"name": "Zhang San",
"age": 28,
"isStudent": false,
"hobbies": ["programming", "reading"],
"address": {
"city": "Beijing",
"zip": "100000"
}
}
2. System.Text.Json の概要
System.Text.Jsonは、.NET Core 3.0 以降に組み込まれている高性能な JSON ライブラリであり、サードパーティ製のパッケージを必要としません。Newtonsoft.Json よりも、こちらを使用することをお勧めします。
using System.Text.Json;
3. シリアライズ:オブジェクトからJSONへの変換
JsonSerializer.Serialize()は、オブジェクトをJSON文字列に変換します。
▶ サンプル
using System;
using System.Text.Json;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsStudent { get; set; }
}
class Program
{
static void Main()
{
var person = new Person
{
Name = "Zhang San",
Age = 28,
IsStudent = false
};
string json = JsonSerializer.Serialize(person);
Console.WriteLine(json);
}
}
{"Name":"Zhang San","Age":28,"IsStudent":false}
4. 逆シリアライズ:JSONからオブジェクトへ
JsonSerializer.Deserialize``<T>``()は、JSON文字列を指定された型のオブジェクトに変換します。
▶ サンプル
using System;
using System.Text.Json;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsStudent { get; set; }
}
class Program
{
static void Main()
{
string json = @"{""Name"":""Zhang San"",""Age"":28,""IsStudent"":false}";
Person person = JsonSerializer.Deserialize<Person>(json);
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
}
}
Name: Zhang San, Age: 28
5. JsonSerializerOptions の設定
JsonSerializerOptionsを使用すると、命名規則、インデント、null値の無視など、シリアライゼーションの挙動を制御できます。
| オプション | 説明 |
|---|---|
PropertyNamingPolicy |
プロパティの命名規則(例:JsonNamingPolicy.CamelCase) |
WriteIndented |
読みやすさのために出力をインデントすべきか |
IgnoreNullValues |
null値のプロパティを無視するかどうか |
PropertyNameCaseInsensitive |
デシリアライズ時にプロパティ名の大文字・小文字を区別しない |
▶ サンプル
using System;
using System.Text.Json;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
}
class Program
{
static void Main()
{
var person = new Person
{
Name = "Zhang San",
Age = 28,
Email = null
};
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true,
IgnoreNullValues = true
};
string json = JsonSerializer.Serialize(person, options);
Console.WriteLine(json);
}
}
{
"name": "Zhang San",
"age": 28
}
6. コレクションと辞書のシリアライズ
JsonSerializerは、リストや辞書などのコレクション型を直接シリアライズすることができます。
▶ サンプル
using System;
using System.Collections.Generic;
using System.Text.Json;
public class Student
{
public string Name { get; set; }
public int Score { get; set; }
}
class Program
{
static void Main()
{
var students = new List<Student>
{
new Student { Name = "Zhang San", Score = 90 },
new Student { Name = "Li Si", Score = 85 },
new Student { Name = "Wang Wu", Score = 92 }
};
var options = new JsonSerializerOptions { WriteIndented = true };
string json = JsonSerializer.Serialize(students, options);
Console.WriteLine(json);
var scores = new Dictionary`<string, int>`
{
["Chinese"] = 90,
["Math"] = 95,
["English"] = 88
};
string dictJson = JsonSerializer.Serialize(scores, options);
Console.WriteLine(dictJson);
}
}
[
{
"Name": "Zhang San",
"Score": 90
},
{
"Name": "Li Si",
"Score": 85
},
{
"Name": "Wang Wu",
"Score": 92
}
]
{
"Chinese": 90,
"Math": 95,
"English": 88
}
7. 動的なJSON:JsonDocumentとJsonElement
JSONの構造が不明な場合は、JsonDocumentおよびJsonElementを使用することで、あらかじめ定義されたクラスを使わずに動的な解析を行うことができます。
▶ サンプル
using System;
using System.Text.Json;
class Program
{
static void Main()
{
string json = @"{""name"":""Zhang San"",""age"":28,""hobbies"":[""programming"",""reading""]}";
using JsonDocument doc = JsonDocument.Parse(json);
JsonElement root = doc.RootElement;
string name = root.GetProperty("name").GetString();
int age = root.GetProperty("age").GetInt32();
JsonElement hobbies = root.GetProperty("hobbies");
Console.WriteLine($"Name: {name}, Age: {age}");
foreach (JsonElement hobby in hobbies.EnumerateArray())
{
Console.WriteLine($"Hobby: {hobby.GetString()}");
}
}
}
Name: Zhang San, Age: 28
Hobby: programming
Hobby: reading
8. カスタムシリアライズ:JsonConverter
JsonConverter<T>を継承し、ReadおよびWriteメソッドをオーバーライドして、カスタムのシリアライズ/デシリアライズロジックを実装します。
▶ サンプル
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
public class DateOnlyConverter : JsonConverter`<DateTime>`
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.ParseExact(reader.GetString(), "yyyy-MM-dd", null);
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString("yyyy-MM-dd"));
}
}
public class Event
{
public string Title { get; set; }
[JsonConverter(typeof(DateOnlyConverter))]
public DateTime Date { get; set; }
}
class Program
{
static void Main()
{
var evt = new Event
{
Title = "Product Launch",
Date = new DateTime(2025, 6, 15)
};
string json = JsonSerializer.Serialize(evt);
Console.WriteLine(json);
Event deserialized = JsonSerializer.Deserialize<Event>(json);
Console.WriteLine($"Title: {deserialized.Title}, Date: {deserialized.Date:yyyy-MM-dd}");
}
}
{"Title":"Product Launch","Date":"2025-06-15"}
Title: Product Launch, Date: 2025-06-15
9. JSONとLINQの組み合わせ
JSONをコレクションにデシリアライズした後、LINQを使用してクエリ、フィルタリング、集計を行うことができます。
▶ サンプル
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
public class Student
{
public string Name { get; set; }
public int Score { get; set; }
public string City { get; set; }
}
class Program
{
static void Main()
{
string json = @"[
{""Name"":""Zhang San"",""Score"":90,""City"":""Beijing""},
{""Name"":""Li Si"",""Score"":85,""City"":""Shanghai""},
{""Name"":""Wang Wu"",""Score"":92,""City"":""Beijing""},
{""Name"":""Zhao Liu"",""Score"":78,""City"":""Shanghai""},
{""Name"":""Qian Qi"",""Score"":95,""City"":""Beijing""}
]";
List<Student> students = JsonSerializer.Deserialize<List<Student>>(json);
var beijingTop = students
.Where(s => s.City == "Beijing")
.OrderByDescending(s => s.Score)
.ToList();
Console.WriteLine("Beijing students (sorted by score, descending):");
foreach (var s in beijingTop)
{
Console.WriteLine($" {s.Name}: {s.Score}");
}
var cityAvg = students
.GroupBy(s => s.City)
.Select(g => new { City = g.Key, Avg = g.Average(s => s.Score) });
Console.WriteLine("\nAverage score by city:");
foreach (var item in cityAvg)
{
Console.WriteLine($" {item.City}: {item.Avg:F1}");
}
}
}
Beijing students (sorted by score, descending):
Qian Qi: 95
Wang Wu: 92
Zhang San: 90
Average score by city:
Beijing: 92.3
Shanghai: 81.5
10. CSVの読み書きの概要
CSV(カンマ区切り値)は、フィールドをカンマで区切るテキスト形式であり、データのエクスポートや交換によく使用されます。C#の標準ライブラリにはCSVの組み込みサポートはありません。手動で解析するか、CsvHelperのようなサードパーティ製ライブラリを使用することができます。
▶ サンプル
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
class Program
{
static void Main()
{
var records = new List<string[]>
{
new[] { "Name", "Age", "City" },
new[] { "Zhang San", "28", "Beijing" },
new[] { "Li Si", "25", "Shanghai" }
};
string csvPath = "data.csv";
File.WriteAllLines(csvPath, records.Select(r => string.Join(",", r)));
string[] lines = File.ReadAllLines(csvPath);
foreach (string line in lines)
{
string[] fields = line.Split(',');
Console.WriteLine($"{fields[0],-10} {fields[1],-4} {fields[2]}");
}
}
}
Name Age City
Zhang San 28 Beijing
Li Si 25 Shanghai
💡 ヒント:本番環境では、CsvHelper(NuGet パッケージ)の使用をお勧めします。このパッケージは、コンマ、引用符、改行を含むフィールドを正しく処理し、手動での解析によくある落とし穴を回避できます。
❓ よくある質問
JsonSerializerOptions内でPropertyNameCaseInsensitive = trueを設定してください。[JsonIgnore]属性を追加することで、そのプロパティをスキップできます。JsonConverter<DateTime> を作成し、Write メソッドをオーバーライドして、希望する日付形式の文字列を指定してください。📖 まとめ
- JSONでは、オブジェクトには
{}、配列には[]が使用されており、汎用的なデータ交換フォーマットです System.Text.Jsonは組み込みの.NET JSONライブラリであり、変換にはSerialize/Deserializeを使用していますJsonSerializerOptions命名規則、インデント、NULL値の扱いなどを設定しますJsonDocument/JsonElementは、構造が不明確なJSONの解析に適しています- カスタム
JsonConverter<T>は、特定の型に対するシリアライズロジックを制御します - コレクションにデシリアライズされたJSONは、LINQと組み合わせてクエリや集計を行うことができます
- CSVは手動で解析することも可能ですが、本番環境での利用には
CsvHelperライブラリの使用をお勧めします
📝 練習問題
Bookクラス(Title、Author、Price、IsPublished)を定義し、それをインデント付きのJSON文字列としてシリアライズして出力した後、再びオブジェクトとしてデシリアライズする- 少なくとも3冊の本を含む
List<Book>を作成し、JSON形式に変換した後、LINQを使用して価格が50を超える本を抽出する DateTimeを"yyyy/MM/dd"形式でシリアライズするカスタムJsonConverterを作成し、デシリアライズによって正しく復元されることを確認するDictionary<string, string>をJSON形式でシリアライズしてファイルに保存し、その後、そのファイルから読み込んでデシリアライズする- コンマを含むフィールドを処理する手動のCSV読み書きロジックを作成する(コンマを二重引用符で囲む)