Como usar Utf8JsonWriter no System.Text.Json

Este artigo mostra como usar o tipo Utf8JsonWriter para criar serializadores personalizados.

Utf8JsonWriter é uma forma de alto desempenho para escrita de texto JSON codificado em UTF-8 com base em tipos .NET comuns como String, Int32 e DateTime. O gravador é um tipo de baixo nível que pode ser usado para criar serializadores personalizados. O método JsonSerializer.Serialize é usado sob as coberturas Utf8JsonWriter.

O exemplo a seguir mostra como usar a classe 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)

Gravar com texto UTF-8

Para obter o melhor desempenho possível ao usar Utf8JsonWriter, o conteúdo JSON de gravação já codificado como texto UTF-8 em vez de como cadeias de caracteres UTF-16. Use JsonEncodedText para armazenar em cache e pré-codificar nomes e valores de propriedade de cadeia de caracteres conhecidos como estáticos e passá-los para o gravador, em vez de usar literais de cadeia de caracteres UTF-16. Isso é mais rápido do que armazenar em cache e usar matrizes de bytes UTF-8.

Essa abordagem também funcionará se você precisar fazer escape personalizado. System.Text.Json não permite desabilitar a fuga ao escrever uma cadeia de caracteres. No entanto, você pode passar seu próprio JavaScriptEncoder personalizado como uma opção para o gravador ou criar o seu próprio JsonEncodedText que usa o JavascriptEncoder para fazer o escape e, em seguida, gravar JsonEncodedTextem vez da cadeia de caracteres. Para obter mais informações, consulte Personalizar a codificação de caracteres.

Gravar JSON bruto

Em alguns cenários, convém gravar JSON "bruto" em um conteúdo JSON que você está criando com Utf8JsonWriter. Você pode usar Utf8JsonWriter.WriteRawValue para fazer isso. Aqui estão cenários típicos:

  • Você tem um conteúdo JSON existente que deseja incluir no novo JSON.

  • Você deseja formatar valores de forma diferente da formatação padrão Utf8JsonWriter.

    Por exemplo, talvez você queira personalizar a formatação numérica. Por padrão, System.Text.Json omite o ponto decimal para números inteiros, escrevendo 1 em vez de 1.0, por exemplo. A lógica é que escrever menos bytes é bom para o desempenho. Mas suponha que o consumidor de seu JSON trate números com decimais como duplos e números sem decimais como inteiros. Talvez você queira garantir que os números em uma matriz sejam todos reconhecidos como duplos, escrevendo um ponto decimal e zero para números inteiros. O seguinte exemplo mostra como fazer isso:

    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
    //    }
    //  ]
    //}
    

Personalizar codificação de caracteres

A configuração StringEscapeHandling de JsonTextWriter oferece opções para escapar de todos os caracteres não ASCII ou caracteres HTML. Por padrão, Utf8JsonWriter escapa todos os caracteres não ASCII e HTML. Esse escape é feita por razões de segurança detalhadas. Para especificar uma política de escape diferente, crie JavaScriptEncoder e defina JsonWriterOptions.Encoder. Para obter mais informações, consulte Personalizar a codificação de caracteres.

Grave valores nulos

Para gravar valores nulos usando Utf8JsonWriter, chame:

  • WriteNull para gravar um par de chaves-valor com nulo como o valor.
  • WriteNullValue para escrever null como um elemento de uma matriz JSON.

Para uma propriedade de cadeia de caracteres, se a cadeia de caracteres for nula, WriteString e WriteStringValue for equivalente a WriteNull e WriteNullValue.

Gravar valores de Timespan, Uri ou char

Para gravar valores Timespan, Uriou char, formatá-los como cadeias de caracteres (chamando ToString(), por exemplo) e chamar WriteStringValue.

Confira também