Como personalizar a codificação de caracteres com System.Text.Json

Por padrão, o serializador escapa de todos os caracteres não ASCII. Ou seja, ele os substitui por \uxxxx, onde xxxx é o código Unicode do caractere. Por exemplo, se a propriedade Summary no JSON a seguir estiver definida como Cirílico жарко, o objeto WeatherForecast será serializado conforme mostrado neste exemplo:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "\u0436\u0430\u0440\u043A\u043E"
}

Serializar conjuntos de caracteres de linguagem

Para serializar os conjuntos de caracteres de uma ou mais linguagens sem escapar, especifique intervalo(s) Unicode ao criar uma instância de System.Text.Encodings.Web.JavaScriptEncoder, conforme mostrado no exemplo a seguir:

using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
Imports System.Text.Encodings.Web
Imports System.Text.Json
Imports System.Text.Unicode
var options1 = new JsonSerializerOptions
{
    Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.Cyrillic),
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options1);
options = New JsonSerializerOptions With {
    .Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.Cyrillic),
    .WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecast1, options)

Este código não escapa de caracteres cirílicos ou gregos. Se a propriedade Summary for definida como Cirílico жарко, o objeto WeatherForecast será serializado conforme mostrado neste exemplo:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "жарко"
}

Por padrão, o codificador é inicializado com o intervalo BasicLatin.

Para serializar todos os conjuntos de idiomas sem escapar, use UnicodeRanges.All.

Serializar caracteres específicos

Uma alternativa é especificar caracteres individuais que você deseja permitir a passagem sem ser escapado. O exemplo a seguir serializa apenas os dois primeiros caracteres de жарко:

using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
Imports System.Text.Encodings.Web
Imports System.Text.Json
Imports System.Text.Unicode
var encoderSettings = new TextEncoderSettings();
encoderSettings.AllowCharacters('\u0436', '\u0430');
encoderSettings.AllowRange(UnicodeRanges.BasicLatin);
var options2 = new JsonSerializerOptions
{
    Encoder = JavaScriptEncoder.Create(encoderSettings),
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options2);
Dim encoderSettings As TextEncoderSettings = New TextEncoderSettings
encoderSettings.AllowCharacters(ChrW(&H436), ChrW(&H430))
encoderSettings.AllowRange(UnicodeRanges.BasicLatin)
options = New JsonSerializerOptions With {
    .Encoder = JavaScriptEncoder.Create(encoderSettings),
    .WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecast1, options)

Aqui está um exemplo de JSON produzido pelo código anterior:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "жа\u0440\u043A\u043E"
}

Listas de bloqueios

As seções anteriores mostram como especificar listas de permissões de pontos de código ou intervalos que você não deseja que escapem. No entanto, há listas de bloqueios globais e específicas do codificador que podem substituir determinados pontos de código na lista de permissões. Os pontos de código em uma lista de bloqueios sempre são escapados, mesmo que estejam incluídos em sua lista de permissões.

Lista de bloqueio global

A lista de bloqueio global inclui coisas como caracteres de uso privado, caracteres de controle, pontos de código indefinidos e determinadas categorias Unicode, como a categoria Space_Separator, excluindo U+0020 SPACE. Por exemplo, U+3000 IDEOGRAPHIC SPACE é escapado mesmo se você especificar símbolos CJK do intervalo Unicode e Pontuação (U+3000-U+303F) como sua lista de permissões.

A lista de bloqueio global é um detalhe de implementação que foi alterado em cada versão do .NET Core e no .NET 5. Não assuma uma dependência de um caractere sendo membro (ou não sendo membro) da lista de bloqueio global.

Listas de bloqueios específicas do codificador

Exemplos de pontos de código bloqueados específicos do codificador incluem '<' e '&' para o codificador HTML, '\' para o codificador JSON e '%' para o codificador de URL. Por exemplo, o codificador HTML sempre escapa de E comerciais ('&'), mesmo que o E comercial no intervalo BasicLatin e todos os codificadores sejam inicializados com BasicLatin por padrão.

Serializar todos os caracteres

Para minimizar o escape, é possível usar o operador JavaScriptEncoder.UnsafeRelaxedJsonEscaping, conforme mostrado no exemplo a seguir:

using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
Imports System.Text.Encodings.Web
Imports System.Text.Json
Imports System.Text.Unicode
var options3 = new JsonSerializerOptions
{
    Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options3);
options = New JsonSerializerOptions With {
    .Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
    .WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecast1, options)

Cuidado

Em comparação com o codificador padrão, o codificador UnsafeRelaxedJsonEscaping é mais permissivo em permitir que os caracteres passem sem escape:

  • Ele não faz escape de caracteres sensíveis a HTML, como <, >, & e '.
  • Ele não oferece proteções adicionais de defesa em profundidade contra XSS ou ataques de divulgação não autorizada de informação, como aqueles que podem resultar da discordância entre o cliente e o servidor sobre o conjunto de caracteres.

Use o codificador não seguro somente quando souber que o cliente interpretará o conteúdo resultante como JSON codificado em UTF-8. Por exemplo, é possível usá-lo se o servidor estiver enviando o cabeçalho de resposta Content-Type: application/json; charset=utf-8. Nunca permita que a saída bruta UnsafeRelaxedJsonEscaping seja emitida em uma página HTML ou em um elemento <script>.

Confira também