はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】System.Text.Jsonで直接UTF-8バイト配列にシリアライズ & UTF-8バイト配列からデシリアライズする方法

はじめに

今回はSystem.Text.Jsonを利用して直接UTF-8バイト配列にシリアライズ & UTF-8バイト配列からデシリアライズする方法を紹介したいと思います。

また公式ドキュメントには文字列ベースのメソッド(UTF-16)を使用するよりもUTF-8バイト配列のシリアル化は約5から10%高速だと表記されています。

UTF-8 バイト配列へのシリアル化は、文字列ベースのメソッドを使用するより約 5 から 10% 高速です。 違いは、バイト (UTF-8) を文字列 (UTF-16) に変換する必要がないことから生じます。

C# で JSON のシリアル化と逆シリアル化を行う方法 - .NET | Microsoft Learn

概要

System.Text.JsonはMicrosoftが提供するハイパフォーマンスなJsonシリアライザーです。

Provides high-performance and low-allocating types that serialize objects to JavaScript Object Notation (JSON) text and deserialize JSON text to objects, with UTF-8 support built-in. Also provides types to read and write JSON text encoded as UTF-8, and to create an in-memory document object model (DOM), that is read-only, for random access of the JSON elements within a structured view of the data.

// DeepL翻訳
オブジェクトをJavaScript Object Notation (JSON)テキストにシリアライズし、JSONテキストをオブジェクトにデシリアライズする、高性能で割り当ての少ない型を提供します。また、UTF-8 でエンコードされた JSON テキストを読み書きする型や、データの構造化されたビュー内で JSON 要素のランダムアクセスを行うための、読み取り専用のインメモリドキュメントオブジェクトモデル (DOM) を作成する型も提供します。

www.nuget.org

使い方

基本的な使い方を書いていきます。

public class Person
{
    public int Age { get; set; }
    public string Name { get; set; }
}
        
public static void Main()
{
    var target = new Person
    {
        Age = 40,
        Name = "John",
    };
            
    // UTF-8のバイト配列にシリアライズ
    // NOTE: stringはUTF-16なので注意
    // SerializeToUtf8Bytesメソッド or Serializeメソッドの引数にUtf8JsonWriter でUTF-8バイト配列にシリアル化
    byte[] data = JsonSerializer.SerializeToUtf8Bytes(target);
            
    // 007b,0022,0041,0067,0065,0022,003a,0034,0030,002c,0022,004e,0061,006d,0065,0022,003a,0022,004a,006f,0068,006e,0022,007d
    // 中身的には「 {"Age":40,"Name":"John"} 」
    Console.WriteLine(string.Join(",", data.Select(x => x.ToString("x4"))));

    // デシリアライズ
    // ReadOnlySpan<byte> or Utf8JsonReader を引数に受け取るとUTF-8から逆シリアル化する
    var person = JsonSerializer.Deserialize<Person>(data.AsSpan());

    Console.WriteLine(person.Age);          // 40
    Console.WriteLine(person.Name);         // John
}

シリアライズ

コメントにも書きましたが、以下の2種類の方法でシリアライズします。

  • JsonSerializer.SerializeToUtf8Bytes
  • JsonSerializer.Serializeの引数にUtf8JsonWriterを渡す
// JsonSerializer.SerializeToUtf8Bytes
byte[] data = JsonSerializer.SerializeToUtf8Bytes(json);

// JsonSerializer.Serialize + Utf8JsonWriter
using MemoryStream stream = new MemoryStream();
using Utf8JsonWriter writer = new Utf8JsonWriter(stream);
// ...
byte[] data = JsonSerializer.SerializeToUtf8Bytes(writer);

デシリアライズ

以下の2種類があります。

  • JsonSerializer.Deserializeの引数にReadOnlySpan<byte>を渡す
  • JsonSerializer.Deserializeの引数にUtf8JsonReaderを渡す
// ReadOnlySpan<byte>
ReadOnlySpan<byte> data = "{\"Age\":40,\"Name\":\"John\"}"u8;
Preson person = JsonSerializer.Deserialize<Person>(data);

// Utf8JsonReader
ReadOnlySpan<byte> data = "{\"Age\":40,\"Name\":\"John\"}"u8;
Utf8JsonReader reader = new Utf8JsonReader(data);
var person = JsonSerializer.Deserialize<Person>(ref reader);

JsonSerializerOptions

JsonSerializerOptionsを利用することで様々なオプションをつけることができます。

learn.microsoft.com