語言

如何在 System.Text.Json 中使用 Utf8JsonWriter

本文說明如何使用 Type Utf8JsonWriter 來建立自訂序列化器。

Utf8JsonWriter 是一種高效能方式,可從常見的 .NET 類型 (例如 String、Int32 與 DateTime) 撰寫 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.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時達到最佳效能,請將 JSON payload 寫入已編碼為 UTF-8 文字,而非 UTF-16 字串。 用 JsonEncodedText 來快取並預先編碼已知字串屬性名稱和值為靜態,然後傳給寫入者,而不是使用 UTF-16 字串字面量。 這比快取並使用 UTF-8 位元組陣列還要快。

如果你需要自訂逃脫,這種方法也適用。 System.Text.Json 寫字串時不允許關閉跳脫功能。 不過,你可以將自己自訂的 JavaScriptEncoder 作為選項傳給寫入器,或者建立你自己的 JsonEncodedText,使用你的 JavascriptEncoder 來進行逸出處理,然後寫入 JsonEncodedText,而不是字串。 欲了解更多資訊,請參閱 自訂字元編碼。

撰寫原始 JSON

在某些情況下,你可能會想把「原始」的 JSON 寫入你正在建立的 Utf8JsonWriterJSON payload。 你可以用 Utf8JsonWriter.WriteRawValue 來做這件事。 以下是典型情境:

  • 你有一個現有的 JSON 有效載荷,想要用新的 JSON 包覆它。

  • 你要讓數值格式和預設 Utf8JsonWriter 格式不同。

    例如,你可能想自訂數字格式。 預設情況下,System.Text.Json 會省略整數的小數點,例如寫成 1,而不是 1.0。 原因在於,寫入較少的位元組有助於提升效能。 但假設你的 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
    //    }
    //  ]
    //}
    

自訂角色逃脫

StringEscapeHandling 設定JsonTextWriter提供逃逸所有非 ASCII 字元或 HTML 字元的選項。 預設情況下, Utf8JsonWriter 跳脫所有非 ASCII 及 HTML 字元。 這項逸出處理是基於縱深防禦的安全考量而進行的。 若要指定不同的逸出原則,請建立 JavaScriptEncoder,並設定 JsonWriterOptions.Encoder。 欲了解更多資訊,請參閱 自訂字元編碼。

寫入空值

若要使用 Utf8JsonWriter 寫入空值,請呼叫:

對於字串性質,若字串為零 WriteString ,則 與 WriteStringValue 等價於 WriteNull 和 WriteNullValue。

撰寫 Timespan、URI 或 char 值

要寫入 Timespan,Urichar或值,將它們格式化為字串(例如呼叫 ToString(),並呼叫 WriteStringValue)。

另請參閱