Language

で Utf8JsonWriter を使用する方法 System.Text.Json

この記事では、カスタム シリアライザーを構築するために Utf8JsonWriter 型を使用する方法について説明します。

Utf8JsonWriter は、String、Int32、DateTime のような一般的な .NET 型から UTF-8 でエンコードされた JSON テキストを書き込むための、ハイパフォーマンスな方法です。 ライターは低レベルの型であり、カスタム シリアライザーを構築するために使用できます。 JsonSerializer.Serialize メソッドは、内部では Utf8JsonWriter を使用します。

次の例は、 Utf8JsonWriter クラスの使用方法を示しています。

var options = new JsonWriterOptions
{
    Indented = true
};

using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream, options);

writer.WriteStartObject();
writer.WriteString("date", DateTimeOffset.UtcNow);
writer.WriteNumber("temp", 42);
writer.WriteEndObject();
writer.Flush();

string json = Encoding.UTF8.GetString(stream.ToArray());
Console.WriteLine(json);
Dim options As JsonWriterOptions = New JsonWriterOptions With {
    .Indented = True
}

Dim stream As MemoryStream = New MemoryStream
Dim writer As Utf8JsonWriter = New Utf8JsonWriter(stream, options)

writer.WriteStartObject()
writer.WriteString("date", DateTimeOffset.UtcNow)
writer.WriteNumber("temp", 42)
writer.WriteEndObject()
writer.Flush()

Dim json As String = Encoding.UTF8.GetString(stream.ToArray())
Console.WriteLine(json)

ライターを再利用する

.NET 11 以降では、Reset(Stream, JsonWriterOptions)またはReset(IBufferWriter<Byte>, JsonWriterOptions)を呼び出してライターを再利用します。 これらのオーバーロードは、別の Utf8JsonWriterを割り当てることなく、変換先とオプションを変更します。

ライターをリセットする前に、現在の JSON ペイロードを完了し、 Flushを呼び出します。 Reset は、writer の状態をクリアしますが、保留中の出力はフラッシュしません。

writer.WriteEndObject();
writer.Flush();

writer.Reset(nextStream, new JsonWriterOptions { Indented = true });
writer.WriteEndObject()
writer.Flush()

writer.Reset(nextStream, New JsonWriterOptions With {.Indented = True})

UTF-8 テキストで書き込む

Utf8JsonWriterの使用中に可能な限り最高のパフォーマンスを実現するには、UTF-16 文字列としてではなく、UTF-8 テキストとして既にエンコードされている JSON ペイロードを書き込みます。 JsonEncodedTextを使用して、既知の文字列プロパティの名前と値を静的としてキャッシュおよび事前エンコードし、UTF-16 文字列リテラルを使用せずにライターに渡します。 これは、キャッシュと UTF-8 バイト配列の使用よりも高速です。

この方法は、カスタム エスケープを実行する必要がある場合にも機能します。 System.Text.Json では、文字列を書き込む際にエスケープを無効にすることはできません。 ただし、ライターにオプションとして独自のカスタム JavaScriptEncoderを渡したり、JavascriptEncoderを使用してエスケープを行う独自のJsonEncodedTextを作成したり、文字列の代わりにJsonEncodedTextを書き込んだりすることもできます。 詳細については、「 文字エンコードのカスタマイズ」を参照してください。

生の JSON を書き込む

シナリオによっては、 Utf8JsonWriterで作成する JSON ペイロードに "raw" JSON を書き込む必要がある場合があります。 これを行うには、 Utf8JsonWriter.WriteRawValue を使用できます。 一般的なシナリオを次に示します。

  • 新しい JSON で包みたい既存の JSON ペイロードがあります。

  • 既定の Utf8JsonWriter 書式設定とは異なる方法で値を書式設定する必要があります。

    たとえば、数値の書式設定をカスタマイズできます。 既定では、System.Text.Jsonは整数の小数点を省略し、たとえば、1.0ではなく1を書き込みます。 その理由は、書き込むバイト数を減らした方がパフォーマンスに優れているということです。 ただし、JSONのコンシューマーが、小数点を含む数値を倍精度浮動小数点数として扱い、小数点を含まない数値を整数として扱うとします。 整数に対して小数点とゼロを書き込むことで、配列内の数値がすべて倍精度浮動小数点として認識されるようにすることができます。 その方法を次の例に示します。

    using System.Text;
    using System.Text.Json;
    
    namespace WriteRawJson;
    
    public class Program
    {
        public static void Main()
        {
            JsonWriterOptions writerOptions = new() { Indented = true, };
    
            using MemoryStream stream = new();
            using Utf8JsonWriter writer = new(stream, writerOptions);
    
            writer.WriteStartObject();
    
            writer.WriteStartArray("defaultJsonFormatting");
            foreach (double number in new double[] { 50.4, 51 })
            {
                writer.WriteStartObject();
                writer.WritePropertyName("value");
                writer.WriteNumberValue(number);
                writer.WriteEndObject();
            }
            writer.WriteEndArray();
    
            writer.WriteStartArray("customJsonFormatting");
            foreach (double result in new double[] { 50.4, 51 })
            {
                writer.WriteStartObject();
                writer.WritePropertyName("value");
                writer.WriteRawValue(
                    FormatNumberValue(result), skipInputValidation: true);
                writer.WriteEndObject();
            }
            writer.WriteEndArray();
    
            writer.WriteEndObject();
            writer.Flush();
    
            string json = Encoding.UTF8.GetString(stream.ToArray());
            Console.WriteLine(json);
        }
        static string FormatNumberValue(double numberValue)
        {
            return numberValue == Convert.ToInt32(numberValue) ? 
                numberValue.ToString() + ".0" : numberValue.ToString();
        }
    }
    // output:
    //{
    //  "defaultJsonFormatting": [
    //    {
    //      "value": 50.4
    //    },
    //    {
    //      "value": 51
    //    }
    //  ],
    //  "customJsonFormatting": [
    //    {
    //      "value": 50.4
    //    },
    //    {
    //      "value": 51.0
    //    }
    //  ]
    //}
    

文字エスケープをカスタマイズする

JsonTextWriterの StringEscapeHandling 設定には、ASCII 以外のすべての文字または HTML 文字をエスケープするオプションが用意されています。 既定では、 Utf8JsonWriter は ASCII 以外 のすべての文字と HTML 文字をエスケープします。 このエスケープは、多層防御のセキュリティ上の理由から行われます。 別のエスケープ ポリシーを指定するには、 JavaScriptEncoder を作成し、 JsonWriterOptions.Encoder設定します。 詳細については、「 文字エンコードのカスタマイズ」を参照してください。

null 値を書き込む

Utf8JsonWriterを使用して null 値を書き込むには、次を呼び出します。

  • WriteNull で値として null を含むキーと値のペアを書き込みます。
  • WriteNullValue JSON 配列の要素として null を書き込む場合。

文字列プロパティの場合、文字列が null の場合、 WriteString と WriteStringValue は WriteNull と WriteNullValueと同じです。

Timespan、Uri、または char の値を書き込む

Timespan、Uri、またはchar値を書き込むには、文字列として書式設定し (たとえば、ToString()を呼び出して)、WriteStringValueを呼び出します。

こちらも参照ください