C#: JSONとシリアライズ

1. JSON形式の基礎

JSON(JavaScript Object Notation)は、構造化されたデータをプレーンテキストとして保存・送信するための軽量なデータ交換形式です。

TEXT 📖 参照専用
{
  "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 よりも、こちらを使用することをお勧めします。

CSHARP
using System.Text.Json;

3. シリアライズ:オブジェクトからJSONへの変換

JsonSerializer.Serialize()は、オブジェクトをJSON文字列に変換します。

▶ サンプル

CSHARP
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);
    }
}
▶ 試してみよう
TEXT 📖 参照専用
{"Name":"Zhang San","Age":28,"IsStudent":false}

4. 逆シリアライズ:JSONからオブジェクトへ

JsonSerializer.Deserialize``<T>``()は、JSON文字列を指定された型のオブジェクトに変換します。

▶ サンプル

CSHARP
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}");
    }
}
▶ 試してみよう
TEXT 📖 参照専用
Name: Zhang San, Age: 28

5. JsonSerializerOptions の設定

JsonSerializerOptionsを使用すると、命名規則、インデント、null値の無視など、シリアライゼーションの挙動を制御できます。

オプション 説明
PropertyNamingPolicy プロパティの命名規則(例:JsonNamingPolicy.CamelCase
WriteIndented 読みやすさのために出力をインデントすべきか
IgnoreNullValues null値のプロパティを無視するかどうか
PropertyNameCaseInsensitive デシリアライズ時にプロパティ名の大文字・小文字を区別しない

▶ サンプル

CSHARP
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);
    }
}
▶ 試してみよう
TEXT 📖 参照専用
{
  "name": "Zhang San",
  "age": 28
}

6. コレクションと辞書のシリアライズ

JsonSerializerは、リストや辞書などのコレクション型を直接シリアライズすることができます。

▶ サンプル

CSHARP
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);
    }
}
▶ 試してみよう
TEXT 📖 参照専用
[
  {
    "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を使用することで、あらかじめ定義されたクラスを使わずに動的な解析を行うことができます。

▶ サンプル

CSHARP
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()}");
        }
    }
}
▶ 試してみよう
TEXT 📖 参照専用
Name: Zhang San, Age: 28
Hobby: programming
Hobby: reading

8. カスタムシリアライズ:JsonConverter

JsonConverter<T>を継承し、ReadおよびWriteメソッドをオーバーライドして、カスタムのシリアライズ/デシリアライズロジックを実装します。

▶ サンプル

CSHARP
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}");
    }
}
▶ 試してみよう
TEXT 📖 参照専用
{"Title":"Product Launch","Date":"2025-06-15"}
Title: Product Launch, Date: 2025-06-15

9. JSONとLINQの組み合わせ

JSONをコレクションにデシリアライズした後、LINQを使用してクエリ、フィルタリング、集計を行うことができます。

▶ サンプル

CSHARP 📖 参照専用
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}");
        }
    }
}
論理コード 41 行(40 行制限超過、参照専用)
TEXT 📖 参照専用
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のようなサードパーティ製ライブラリを使用することができます。

▶ サンプル

CSHARP
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]}");
        }
    }
}
▶ 試してみよう
TEXT 📖 参照専用
Name       Age   City
Zhang San  28    Beijing
Li Si      25    Shanghai

💡 ヒント:本番環境では、CsvHelper(NuGet パッケージ)の使用をお勧めします。このパッケージは、コンマ、引用符、改行を含むフィールドを正しく処理し、手動での解析によくある落とし穴を回避できます。

❓ よくある質問

Q System.Text.Json と Newtonsoft.Json の違いは何ですか?
A System.Text.Json は .NET に組み込まれている高性能なライブラリであるのに対し、Newtonsoft.Json はより豊富な機能を備えたサードパーティ製のライブラリです。新しいプロジェクトでは、組み込みのライブラリを使用することをお勧めします。
Q デシリアライズ時にプロパティ名の大文字・小文字が一致しない場合はどうなりますか?
A 大文字・小文字の違いを無視するには、JsonSerializerOptions内でPropertyNameCaseInsensitive = trueを設定してください。
Q プロパティをシリアライズ対象から除外するにはどうすればよいですか?
A そのプロパティに[JsonIgnore]属性を追加することで、そのプロパティをスキップできます。
Q JSON での日付形式をどのように制御すればよいですか?
A カスタムクラス JsonConverter<DateTime> を作成し、Write メソッドをオーバーライドして、希望する日付形式の文字列を指定してください。

📖 まとめ

📝 練習問題

  1. Bookクラス(Title、Author、Price、IsPublished)を定義し、それをインデント付きのJSON文字列としてシリアライズして出力した後、再びオブジェクトとしてデシリアライズする
  2. 少なくとも3冊の本を含むList<Book>を作成し、JSON形式に変換した後、LINQを使用して価格が50を超える本を抽出する
  3. DateTime"yyyy/MM/dd"形式でシリアライズするカスタムJsonConverterを作成し、デシリアライズによって正しく復元されることを確認する
  4. Dictionary<string, string>をJSON形式でシリアライズしてファイルに保存し、その後、そのファイルから読み込んでデシリアライズする
  5. コンマを含むフィールドを処理する手動のCSV読み書きロジックを作成する(コンマを二重引用符で囲む)
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%