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

本文介绍如何使用 Utf8JsonWriter 类型生成自定义序列化程序。

Utf8JsonWriter 是一种高性能方式,从常见 .NET 类型(例如,StringInt32DateTime)编写 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)

使用 UTF-8 文本进行编写

若要在使用 Utf8JsonWriter 时实现可能的最佳性能,请编写已编码为 UTF-8 文本(而不是 UTF-16 字符串)的 JSON 有效负载。 使用 JsonEncodedText 可缓存已知字符串属性名称和值并预先编码为静态,并将这些内容传递给编写器,而不是使用 UTF-16 字符串文本。 这比缓存并使用 UTF-8 字节数组更快。

如果需要进行自定义转义,此方法也适用。 System.Text.Json 不允许在编写字符串时禁用转义。 但是,可以将自己的自定义 JavaScriptEncoder 作为一个选项传入编写器,或创建自己的 JsonEncodedText 以使用你的 JavascriptEncoder 进行转义,然后编写 JsonEncodedText 而不是字符串。 有关详细信息,请参阅自定义字符编码

写入原始 JSON

在某些情况下,可能需要将“原始”JSON 写入使用 Utf8JsonWriter 创建的 JSON 有效负载。 可使用 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
    //    }
    //  ]
    //}
    

自定义字符转义

JsonTextWriterStringEscapeHandling 设置提供用于转移所有非 ASCII 字符或 HTML 字符的选项。 默认情况下,Utf8JsonWriter 会转义所有非 ASCII 和 HTML 字符。 进行此转义是出于深度防御安全原因。 若要指定不同的转义策略,请创建 JavaScriptEncoder 并设置 JsonWriterOptions.Encoder。 有关详细信息,请参阅自定义字符编码

编写 null 值

若要使用 Utf8JsonWriter 编写 null 值,请调用:

  • WriteNull,用于将具有 null 的键值对编写为值。
  • WriteNullValue用于将 null 编写为 JSON 数组的元素。

对于字符串属性,如果字符串为 null,则 WriteStringWriteStringValue 等效于 WriteNullWriteNullValue

编写 Timespan、Uri 或 char 值

若要写入 TimespanUrichar 值,请将这些值格式化为字符串(例如,通过调用 ToString())并调用 WriteStringValue

另请参阅