C#(.NET)でJsonの操作はJsonSerializerを使いましょう

この記事の目的は初心者向けC#(.NET)のJsonSerializerの使い方の紹介です。NewtonSoft Json.NETの方が機能が多くて、場合によってパフォーマンスの差もでるかもしれません。

概要

以前、C#(.NET)にはJsonフォーマットを操作するライブラリが完備されなかったため、3rd partyのライブラリはよく使われる気がします(例えばNewtonSoftのJson.NETライブラリ)。最近、C#のデフォルトJson操作ライブラリが使いやすくなっているようですので、できればスタンダードのライブラリを使うべきだと思ってます。

テスト環境

  • System : Windows 10 (.NET 5)
  • Editor : VSCode

プロジェクトの作成

コマンドラインツール(cmdなど)で下記のコマンドを使って新しいTestというプロジェクトが作成されます。

dotnet new console -n Test

名前空間を宣言する

これから使う名前空間を先に宣言します。

// Created by default, you can delete this line if you want  
using System;  
  
// For File, FileStream, StreamWriter, StreamReader  
using System.IO;  
  
// For Json Serialization and Deserialization  
using System.Text.Json;  
using System.Text.Json.Serialization;  
  
// We use List<T> to contain student information objects  
using System.Collections.Generic;

データのクラスを定義する(例:学生個人情報)

// Define a simple student class with basic parameters and constructor
public class Student
{
    public string Name { get; set; } = string.Empty;
    public string ID { get; set; } = string.Empty;
    public int Age { get; set; } = 0;

    public Student(string name, string id, int age)
    {
        Name = name;
        ID = id;
        Age = age;
    }
}

DataをClassにし、JsonSerializerでSerializeする

static void WriteJson()
{
    // Create a list container for student information 
    List<Student> list = new List<Student>();

    // Adding student information into list
    list.Add(new Student("Sakamoto", "A002", 12));
    list.Add(new Student("John", "B013", 15));
    list.Add(new Student("Lucy", "C024", 13));

    // Make JsonSerializer not to escape non-ASCII character(like Japanese characters). 
    // If you use English only, the "options" parameter is not needed. 
    JsonSerializerOptions options = new JsonSerializerOptions() {
        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.
        Create(System.Text.Unicode.UnicodeRanges.All)};

    // Create a new file and get the FileStream in a "using" scope
    using (FileStream fileStream = File.Create("StudentInfo.json"))
    {
        // Create a StreamWriter for the FileStream in a "using" scope
        using (StreamWriter writer = new StreamWriter(fileStream, System.Text.Encoding.UTF8))
        {
            // Write student information into file in a loop
            foreach (var student in list)
            { 
                // Serialize student information. If the internal string members are English only, the "options" is not needed.
                string str = JsonSerializer.Serialize<Student>(student, options);
                writer.WriteLine(str);
            }

            // Extra explanation :
            // It's safer to use a using scope here,
            // since both FileStream and StreamWriter expicitly implement "IDisposable" interface,
            // which "freeing, releasing, or resetting unmanaged resources" (async version : IAsyncDisposable/DisposeAsync).
            // 
            // The "using" scope helps dealing with Dispose() automatically (even when an exception is thrown),
            // because it is translated to try{} finally{x.Dispose()} in compiler.
        }
    }
}

 

// Create a list container for student information 
List<Student> list = new List<Student>();

// Adding student information into list
list.Add(new Student("Sakamoto", "A002", 12));
list.Add(new Student("John", "B013", 15));
list.Add(new Student("Lucy", "C024", 13));

学生個人情報用のリストを作って、学生情報を作りながらリストに入れる。

 

JsonSerializerOptions options = new JsonSerializerOptions() {
    Encoder = System.Text.Encodings.Web.JavaScriptEncoder.
    Create(System.Text.Unicode.UnicodeRanges.All)};

すべての文字をそのまま保存します。

JsonSerializerはASCIIじゃない文字を勝手に\uxxxのようにUnicodeデータとして書き換えます。英語は問題ないですが、日本語などの文字を読めるように保存したい場合はこの行のoptionsが必要です。
 

using (FileStream fileStream = File.Create("StudentInfo.json"))

学生個人情報のJsonファイルを作成します。
 

using (StreamWriter writer = new StreamWriter(fileStream, System.Text.Encoding.UTF8))

Jsonファイルにデータを書き込むライター(念のためUTF8を指定する)を定義します。

 

string str = JsonSerializer.Serialize<Student>(student, options);
writer.WriteLine(str);

JsonSerializerのドキュメンテーションはこちら:JsonSerializer Class Doc
生徒の情報をSerialize()し、返した文字列をStreamWriter経由Jsonファイルに書き込みます。(英語以外の言語文字じゃなければ、先に定義したoptionsをいれなくても大丈夫です。)
JsonSerializerOptionsはいろんなオプションがあるので、ドキュメンテーションを参考してください:JsonSerializerOptions Doc

 

プロジェクトパスにコマンドラインツールでdotnet runを実行したら、同じパスにStudentInfo.jsonというファイルが作られて、中身はこういう感じ:

{"Name":"Sakamoto","ID":"A002","Age":12}
{"Name":"John","ID":"B013","Age":15}
{"Name":"Lucy","ID":"C024","Age":13}

 

JsonファイルのデータをJsonSerializerでDeserializeして、プリントする

static void ReadJson()
{
    // Open json file and get the FileStream in a "using" scope
    using (FileStream fileStream = File.OpenRead("StudentInfo.json"))
    {
        // Create a StreamReader for the FileStream in a "using" scope
        using (StreamReader reader = new StreamReader(fileStream, System.Text.Encoding.UTF8))
        {
            // Operate in a while loop, which checks if it reaches the end of the stream
            while (!reader.EndOfStream)
            {
                // Deserialize data from line by line and print it
                Student student = JsonSerializer.Deserialize<Student>(reader.ReadLine());
                Console.WriteLine($"Name:{student.Name}, ID:{student.ID}, Age:{student.Age}");
            }
        }
    }
}

 

using (FileStream fileStream = File.OpenRead("StudentInfo.json"))

先ほど作ったStudentInfo.jsonを開いて、それのFileStreamをもらいます。
 

using (StreamReader reader = new StreamReader(fileStream, System.Text.Encoding.UTF8))

Jsonファイルにデータを読みだすリーダー(念のためUTF8を指定する)を定義します。
 

while (!reader.EndOfStream)

読み終わったかどうかをループでチェックします。
 

Student student = JsonSerializer.Deserialize<Student>(reader.ReadLine());
Console.WriteLine($"Name:{student.Name}, ID:{student.ID}, Age:{student.Age}");

最後は、DeserializeしたデータをStudentクラスオブジェクトに返して、中身をプリントします。プリントした結果はこんな感じ:

Name:Sakamoto, ID:A002, Age:12
Name:John, ID:B013, Age:15
Name:Lucy, ID:C024, Age:13

 

まとめ

JsonSerializerクラスを使って、データを簡単に整えたり、操作できます。StreamWriterStreamReaderなども活用し、やれることが多くなる気がします。そしてJsonSerializerOptionsでカスタマイズできるオプションも多いし、更に自由度がありそうです。C#(.NET)でJsonSerializerを使って普通のデータ、I/O処理などは楽だと思います。