【C# 序列化】Json序列化时中文的字符编码 问题
博观而约取,厚积而薄发。这篇文章主要讲述System.Text.Json 中的字符编码相关的知识,希望能为你提供帮助。
参考链接:https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json-character-encoding
System.Text.Json 中的字符编码
Intro
默认的 System.Text.Json
序列化的时候会把所有的非 ASCII 的字符进行转义,这就会导致很多时候我们的一些非 ASCII 的字符就会变成 \\uxxxx
这样的形式,很多场景下并不太友好,我们可以配置字符编码来解决被转义的问题
Sample
首先来看一个简单的示例:
var testObj = new
{
Name = "小明",
Age = 10
};
WriteLine(JsonSerializer.Serialize(testObj));
输出结果如下:
{"Name":"\\u5C0F\\u660E","Age":10}
可以看到,我们的中文没有直接显式出来,而是被转义了,这可读性一下子就大打折扣了,接着我们来尝试让他工作
在我们序列化的时候,可以指定一个 JsonSerializeOptions
,而这个 JsonSerializeOptions
中有一个 Encoder
我们可以用来配置支持的字符编码,不支持的就会被转义,而默认只支持 ASCII 字符
我们可以配置 Encoder
来支持中文,如下所示:
WriteLine(JsonSerializer.Serialize(testObj, new JsonSerializerOptions()
{
Encoder = javascriptEncoder.Create(UnicodeRanges.BasicLatin, new UnicodeRange(0x4E00, 8000))
}));
WriteLine(JsonSerializer.Serialize(testObj, new JsonSerializerOptions()
{
Encoder = javaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs)
}));
WriteLine(JsonSerializer.Serialize(testObj, new JsonSerializerOptions()
{
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
}));
我们可以通过 JavaScriptEncoder.Create
来自定义支持的字符范围,要指定一个 Unicode 范围
在 System.Text.Unicode.UnicodeRanges
这个静态类中定义了一些静态属性来比较方便的使用各个语言对应的字符集范围,当然如果你明确的知道字符从哪里开始,有多个个字符也可以自定义,如上面的第一种方式,但是这种不是特别推荐,因为你知道的范围并不一定是最准确的而且可能会有变更
推荐还是直接使用 UnicodeRanges
里定义好的字符集,如第二种方式,第二种方式这里的 UnicodeRanges.CjkUnifiedIdeographs
就包含了中文字符,去网上查了一下这个 CjkUnifiedIdeographs
代表中文(Chinese)、日文(Japanese )、韩文(Korean)的字符集合
中日韩统一表意文字(英语:CJK Unified Ideographs),也称统一汉字、统汉码(英语:Unihan),目的是要把分别来自中文、日文、韩文、越南文、壮文、琉球文中,起源相同、本义相同、形状一样或稍异的表意文字,在ISO 10646及万国码标准赋予相同编码。
如果你不确定是哪种字符集或者有全球化的需求,可以直接使用 UnicodeRanges.All
来支持所有的字符,如上面第三种方式
上面的示例输出结果如下:
{"Name":"小\\u660E","Age":10}
{"Name":"小明","Age":10}
{"Name":"小明","Age":10}
除此之外还有一个地方可能会需要,对于一些包含 html 标签的文本即使指定了所有字符集也会被转义,这是出于安全考虑。如果觉得不需要转义也可以配置,配置使用 JavaScriptEncoder.UnsafeRelaxedJsonEscaping
即可,示例如下:
var testObj = new
{
Name = "小明",
Age = 10,
Description = "<h1>这是标题</h1>"
};
WriteLine(JsonSerializer.Serialize(testObj, new JsonSerializerOptions()
{
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
}));
WriteLine(JsonSerializer.Serialize(testObj, new JsonSerializerOptions()
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
}));
输出结果如下:
{"Name":"小明","Age":10,"Description":"\\u003Ch1\\u003E这是标题\\u003C/h1\\u003E"}
{"Name":"小明","Age":10,"Description":"<h1>这是标题</h1>"}
Console Logging
之前曾经介绍过 JsonLogging
.NET5 的一个新特性,可以参考 .net 5.0 中的 JsonConsoleJsonConsole
来格式化 Console 的日志为 Json,使用默认的配置然后会发现日志中会有很多这种 \\uxxxx
的东西,看起来怪怪的,如下图所示:
后来测试发现默认支持的字符集也只是 ASCII 字符,而且有些字符会出于安全考虑也会转义,微软也是支持配置 Encoder
的,不过不是 JsonSerializeOptions
, 而是 JsonWriterOptions
,我们可以在注册 JsonConsole
的时候配置 Encoder
,示例如下:
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddJsonConsole(options =>
{
options.JsonWriterOptions = new JsonWriterOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
});
这里我们直接使用比较宽松的转义规则,因为我们的场景是日志不会直接渲染在页面上,所以个人觉得不必要求的太严格
加了上面的配置之后的日志如下:
这样的日志看起来就舒服多了,可读性也会更好一些
More
在指定 Unicode 字符集范围的时候需要把 UnicodeRanges.BasicLatin
也加上,其他的字符串没有包含基本的 ASCII 字符,否则基本的 ASCII 字符也会被转义
你也可以通过 TextEncoderSettings
来配置只支持的字符,示例如下:
var encoderSettings = new TextEncoderSettings();
encoderSettings.AllowCharacters('\\u0436', '\\u0430');
encoderSettings.AllowRange(UnicodeRanges.BasicLatin);
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.Create(encoderSettings)
};
var jsonString = JsonSerializer.Serialize(weatherForecast, options);
针对 JavaScriptEncoder.UnsafeRelaxedJsonEscaping
的使用需要注意安全问题
-
它不转义 HTML 敏感字符,如
<
、>
、&
和'
。 -
它不提供任何针对 XSS 或信息泄露攻击(如客户端和服务器在字符集方面不一致所可能导致的攻击)的额外深度防御保护。
不要将原始 UnsafeRelaxedJsonEscaping
序列化的结果用到 HTML 页面或 <script>
元素
References
-
https://github.com/WeihanLi/SparkTodo/blob/master/SparkTodo.API/Program.cs#L22
-
https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-character-encoding?WT.mc_id=DT-MVP-5004222
-
https://github.com/WeihanLi/SamplesInPractice/blob/master/JsonSample/SystemTextJsonSample/EncoderSample.cs
-
https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json-character-encoding?WT.mc_id=DT-MVP-5004222
-
https://en.wikipedia.org/wiki/CJK_Unified_Ideographs