bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] アプリケーションのデータをJSONにシリアライズする - JsonSerializer

[C#] アプリケーションのデータをJSONにシリアライズする - JsonSerializer

Jan 18, 2026 C# , .NET bucket-sort

アプリケーションのデータを JSON にシリアライズして保存する手順を整理しておきます。

JSON は人間が読めて、差分管理もしやすく、他言語・他環境でも扱いやすいので、設定やユーザーデータの永続化に向いています。ただし「なんでも JSON に突っ込む」だけだと、将来の変更で壊れたり、サイズや速度で困ったりするので、最低限の設計と運用の型を作っておくのが大事です。

初期の .NET Framework の頃は XML が標準的なファイル形式だったようですが、最近の .NET は JSON に移行してきているとか、いないとか…?

例題

日付と数量のペアを、日付の名前のJSONファイルに保存します。

喩えるなら「何月何日の売上個数」みたいなデータですね。

2026-02-16.json

{
  "Date": "2026-02-16",
  "Total": 73799
}

DTO を用意する

アプリ内部のクラスをそのまま保存形式にすると、内部実装の変更が保存形式に波及して壊れやすくなります。 なので、保存用に DTO(Data Transfer Object) を用意して、「保存に必要な最低限の形」を決めます。

public sealed class StatsStore
{
    private sealed class DailyStatsDto
    {
        public string Date { get; set; } = string.Empty;
        public long Total { get; set; }
    }

保存先ディレクトリを定義する

以下のディレクトリを保存先とします:
C:\User\<username>\AppData\Roaming\<appName>\stats

    private readonly string _baseDir;
    public string StatsDirectory => _baseDir;

    public StatsStore(string appName)
    {
        // コンストラクタで保存先ディレクトリをセットします。
        var root = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
        _baseDir = System.IO.Path.Combine(root, appName, "stats");
        Directory.CreateDirectory(_baseDir);
    }    

JSON ファイルに保存する

シリアライズは DTO オブジェクトを渡すだけ。

using System.Text.Json;

    public void SaveTotal(DateOnly date, long total)
    {
        var path = GetPath(date);
        var dto = new DailyStatsDto
        {
            Date = date.ToString("yyyy-MM-dd"),
            Total = total
        };
        var json = JsonSerializer.Serialize(dto, JsonOptions);
        File.WriteAllText(path, json);
    }

JSON ファイルから読み出す

アンシリアライズはジェネリックに DTO クラスを渡すだけ。

    public long LoadTotal(DateOnly date)
    {
        var path = GetPath(date);
        if (!File.Exists(path)) return 0;

        try
        {
            var json = File.ReadAllText(path);
            var dto = JsonSerializer.Deserialize<DailyStatsDto>(json);
            return dto?.Total ?? 0;
        }
        catch
        {
            return 0; // 読み取りエラー時はゼロを返す
        }
    }

クラス実装

public sealed class StatsStore
{
    private sealed class DailyStatsDto
    {
        public string Date { get; set; } = string.Empty;
        public long Total { get; set; }
    }

    private readonly string _baseDir;

    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        WriteIndented = true
    };

    public string StatsDirectory => _baseDir;

    public StatsStore(string appName)
    {
        var root = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
        _baseDir = System.IO.Path.Combine(root, appName, "stats");
        Directory.CreateDirectory(_baseDir);
    }

    public long LoadTotal(DateOnly date)
    {
        var path = GetPath(date);
        if (!File.Exists(path))
        {
            return 0;
        }

        try
        {
            var json = File.ReadAllText(path);
            var dto = System.Text.Json.JsonSerializer.Deserialize<DailyStatsDto>(json);
            return dto?.Total ?? 0;
        }
        catch
        {
            return 0; // 読み取りエラー時はゼロを返す
        }
    }

    public void SaveTotal(DateOnly date, long total)
    {
        var path = GetPath(date);
        var dto = new DailyStatsDto
        {
            Date = date.ToString("yyyy-MM-dd"),
            Total = total
        };
        var json = JsonSerializer.Serialize(dto, JsonOptions);
        File.WriteAllText(path, json);
    }

    private string GetPath(DateOnly date)
        => Path.Combine(_baseDir, $"{date:yyyy-MM-dd}.json");
}

テストコード

// 永続化ストア
StatsStore store = new StatsStore("SampleJson");

// 2025年12月1日から31日までのダミーデータを保存
var startDate = new DateOnly(2025, 12, 1);
var endDate = new DateOnly(2025, 12, 31);

Console.WriteLine($"Saving data from {startDate} to {endDate}...");

for (var date = startDate; date <= endDate; date = date.AddDays(1))
{
    // ダミーデータ: 日付に基づいた値を生成(例: 10000 + 日 * 1000)
    long total = 10000 + date.Day * 1000;
    store.SaveTotal(date, total);
    Console.WriteLine($"Saved {date}: {total}");
}

Console.WriteLine("\nLoading some samples:");
Console.WriteLine("2025-12-01 total: " + store.LoadTotal(new DateOnly(2025, 12, 1)));
Console.WriteLine("2025-12-15 total: " + store.LoadTotal(new DateOnly(2025, 12, 15)));
Console.WriteLine("2025-12-31 total: " + store.LoadTotal(new DateOnly(2025, 12, 31)));

まとめ

System.Text.Json.JsonSerializer が良い仕事をしてくれていますね。

発展

1. 指定期間の平均値を求める

    /// <summary>
    /// start〜end(両端含む)に存在する日だけ平均。0日なら null。
    /// </summary>
    public double? ComputeAverage(DateOnly start, DateOnly end)
    {
        if (end < start) return null;
        var totals = new List<long>();

        for (var d = start; d <= end; d = d.AddDays(1))
        {
            var path = GetPath(d);
            if (!File.Exists(path)) continue;
            var t = LoadTotal(d);
            totals.Add(t);
        }

        if (totals.Count == 0) return null;
        return totals.Average(x => (double)x);
    }

テストコード

var avg1 = store.ComputeAverage(
        new DateOnly(2025, 12, 1),
        new DateOnly(2025, 12, 10));
Console.WriteLine($"Average (2025-12-01 to 2025-12-10): {avg1}");

2. 列挙可能コレクションを返す

    /// <summary>
    /// foreachで回せるようにする
    /// </summary>
    public IEnumerable<(DateOnly Date, long Total)>
        LoadDailyTotals(DateOnly from, DateOnly to)
    {
        if (to < from) yield break;
        for (var d = from; d <= to; d = d.AddDays(1))
            yield return (d, LoadTotal(d));
    }

テストコード

var dailyData = store.LoadDailyTotals(
        new DateOnly(2025, 12, 1),
        new DateOnly(2025, 12, 7));
foreach (var (date, total) in dailyData)
{
    Console.WriteLine($"{date}: {total}");
}

参考サイト

JsonSerializer Class (System.Text.Json) | Microsoft Learn
https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializer?view=net-10.0

C# .NET Windows Code Snippet
← [C#] NotifyIconを使ってアプリをタスクトレイに収納する [C#] アプリケーションの設定をJSON形式で保存する - JsonSerializer →

Related Posts

  • [C#] グローバルフックとキーボードフックを理解する - SetWindowsHookExの使い方 Jan 25, 2026
  • [C#] .NETが用意しているアプリケーションの設定クラスを試す - ConfigurationBuilder Jan 20, 2026
  • [C#] アプリケーションの設定をJSON形式で保存する - JsonSerializer Jan 19, 2026
  • [C#] LiveChartsCoreで折れ線グラフを表示する(基本からレンジ切替まで) Jan 26, 2026

Table of Contents

  • 例題
  • DTO を用意する
  • 保存先ディレクトリを定義する
  • JSON ファイルに保存する
  • JSON ファイルから読み出す
  • クラス実装
  • テストコード
  • まとめ
  • 発展
    • 1. 指定期間の平均値を求める
    • 2. 列挙可能コレクションを返す
  • 参考サイト

Recent Posts

  • Laravel の Event / Listener で Pub/Sub を実装する Apr 2, 2026
  • [C#] delegate と event の仕組みを整理する Apr 1, 2026
  • Pub/Sub パターンとは何か Mar 31, 2026
  • PHP/Laravel で値の状態を判定するヘルパ関数まとめ Mar 30, 2026
  • Filament の dehydrated メソッドとは何か Mar 29, 2026

Categories

  • AWS27
  • C#22
  • .NET20
  • Laravel16
  • Linux12
  • Apache8
  • MySQL8
  • PHP8
  • DynamoDB6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • セキュリティ3
  • .NET Framework1
  • Aurora1
  • Filament1
  • Git1
  • SQS1

Tags

  • AWS
  • C#
  • .NET
  • Laravel
  • PHP
  • MySQL
  • セキュリティ
  • Linux
  • Apache
  • Code Snippet
  • DynamoDB
  • NoSQL
  • PHP-FPM
  • RDS
  • DoS
  • Nginx
  • Windows
  • WordPress
  • パフォーマンス
  • 監視
  • Amazon Linux 2023
  • CMS
  • Docker
  • Ipset
  • Iptables
  • OPCache
  • Webサーバー
  • 認可
  • Aurora
  • Blade
Powered by Hugo & Explore Theme.