如何使用 System.Text.Json 自定义字符编码
默认情况下,序列化程序会转义所有非 ASCII 字符。 即,会将它们替换为 \uxxxx
,其中 xxxx
为字符的 Unicode 代码。 例如,如果以下 JSON 中的 Summary
属性设置为西里尔文 жарко
,则 WeatherForecast
对象会进行序列化,如以下示例中所示:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "\u0436\u0430\u0440\u043A\u043E"
}
序列化语言字符集
若要序列化一种或多种语言的字符集而不进行转义,请在创建 System.Text.Encodings.Web.JavaScriptEncoder 的实例时指定 Unicode 范围,如以下示例中所示:
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)
此代码不转义西里尔文或希腊语字符。 如果 Summary
属性设置为西里尔文 жарко
,则 WeatherForecast
对象会进行序列化,如以下示例中所示:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "жарко"
}
默认情况下,编码器使用 BasicLatin 范围进行初始化。
若要序列化所有语言集而不进行转义,请使用 UnicodeRanges.All。
序列化特定字符
一种替代方法是指定要允许的单个字符,而不进行转义。 下面的示例仅序列化 жарко
的前两个字符:
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)
下面是前面代码生成的 JSON 的示例:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "жа\u0440\u043A\u043E"
}
阻止列表
前面的部分显示了如何指定不希望转义的码位或范围的允许列表。 但是,存在一些全局和编码器特定的阻止列表,它们可覆盖允许列表中的某些码位。 阻止列表中的码位总是进行转义,即使它们包含在允许列表中也是如此。
全局阻止列表
全局阻止列表包括专用字符、控制字符、未定义的码位和某些 Unicode 类别(如 Space_Separator 类别)等内容,但不包含 U+0020 SPACE
。 例如,即使指定 Unicode 范围 CJK 符号和标点 (U+3000-U+303F) 作为允许列表,也会对 U+3000 IDEOGRAPHIC SPACE
进行转义。
全局阻止列表是一个实现细节,在 .NET 的每个版本中都有所更改。 不要依赖于全局阻止列表中包含(或未包含)的字符。
编码器特定的阻止列表
对于 HTML 编码器,编码器特定的阻止列表的示例包括 '<'
和 '&'
;对于 JSON 编码器,示例包括 '\'
;对于 URL 编码器,示例包括 '%'
。 例如,HTML 编码器总是会转义与号 ('&'
),即使与号在 BasicLatin
范围内,并且所有编码器都默认使用 BasicLatin
进行初始化。
序列化所有字符
要最大程度地减少转义,则可以使用 JavaScriptEncoder.UnsafeRelaxedJsonEscaping,如下例所示:
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)
注意
与默认编码器相比,UnsafeRelaxedJsonEscaping
编码器在允许字符通过而不进行转义方面更加宽松:
- 它不转义 HTML 敏感字符,如
<
、>
、&
和'
。 - 它不提供任何针对 XSS 或信息泄露攻击(如客户端和服务器在字符集方面不一致所可能导致的攻击)的额外深度防御保护。
仅当知道客户端将生成的有效负载解释为 UTF-8 编码的 JSON 时,才使用不安全编码器。 例如,如果服务器在发送响应标头 Content-Type: application/json; charset=utf-8
,则可以使用它。 永远不允许将原始 UnsafeRelaxedJsonEscaping
输出发出到 HTML 页面或 <script>
元素。