其实官方文档也说明了很多问题,其实多多翻翻还是能了解不少东西
一、返回自定义的时间格式
直接看官方文档
按照官方文档System.Text.Json
对于时间的支持只能保持ISO 8601-1:2019
,所以如果想返回此外的格式(如:yyyy-MM-dd HH:mm:ss),则需要我们自定义转换器
public class DateTimeConverter : JsonConverter<DateTime>
{
public string DateTimeFormat { get; set; } = "yyyy-MM-dd HH:mm:ss";
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => DateTime.Parse(reader.GetString());
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString(this.DateTimeFormat));
}
Startup.cs
使用转换器:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddJsonOptions(op =>
{
op.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
});
}
二、对于emoji和特殊文字的编码
1.情况
Core 3及以上会默认使用System.Text.Json
来进行序列化,而他会导致一些特殊符号序列化异常
数据库:
Api返回:
2.确认原因
先通过编写一个简单的程序,看到Newtonsoft.Json
和System.Text.Json
编码的区别
static void Main(string[] args)
{
JsonSerializerOptions options = new JsonSerializerOptions()
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true
};
Console.WriteLine("sжаркоt+ri中文ng🐟𐓏𐓘😀 𐓻𐓘𐓻𐓟 𐒻𐓟+++++-*/[^\\.]*🐟");
var model = new model1 { re = "sжаркоt+ri中文ng🐟𐓏𐓘😀 𐓻𐓘𐓻𐓟 𐒻𐓟+++++-*/[^\\.]*🐟\uD83D\uDC1F" };
var sysJson = System.Text.Json.JsonSerializer.Serialize(model, options);
var netJson = Newtonsoft.Json.JsonConvert.SerializeObject(model);
Console.WriteLine(sysJson);
Console.WriteLine(netJson);
Console.ReadKey();
}
通过以上可以看到,System.Text.Json
对一些国家文字还算支持,但对一些特殊符号或emoji是不太支持的,他们会转移成两个\u****
的格式
在github的runtime
上虽然说有人提出这个问题 #42847
#54193
但根据回复,可以看出,System.Text.Json
的设计理念是重点考虑性能、安全之类的方向。
所以这里为了其安全性,是不对多个转义符进行转义
其原因是不会扫描整个String去解析emoji这种特殊编码,而且对于转义的传参是危险的,如XSS
之类的情景下
所以他选择将这种处理方式交给开发者
3.解决方式
1).自定义JavaScriptEncoder
GrabYourPitchforks
留了一段可以escaping字符的代码
折叠代码
using System;
using System.Buffers;
using System.Text;
using System.Text.Encodings.Web;
namespace MyApp
{
class Program
{
static void Main(string[] args)
{
MyEncoder encoder = new MyEncoder();
string input = "Hello there! 😃😺";
string output = encoder.Encode(input);
Console.WriteLine(output);
}
}
// Any Encoder must override the four methods shown below.
unsafe class MyEncoder : JavaScriptEncoder
{
public override int MaxOutputCharactersPerInputCharacter => JavaScriptEncoder.Default.MaxOutputCharactersPerInputCharacter;
public override unsafe int FindFirstCharacterToEncode(char* text, int textLength)
{
ReadOnlySpan<char> input = new ReadOnlySpan<char>(text, textLength);
int idx = 0;
// Enumerate until we're out of data or saw invalid input
while (Rune.DecodeFromUtf16(input.Slice(idx), out Rune result, out int charsConsumed) == OperationStatus.Done)
{
if (WillEncode(result.Value)) { break; } // found a char that needs to be escaped
idx += charsConsumed;
}
if (idx == input.Length) { return -1; } // walked entire input without finding a char which needs escaping
return idx;
}
public override bool WillEncode(int unicodeScalar)
{
// Allow U+1F603 SMILING FACE WITH OPEN MOUTH ('😃'),
// and for all other chars defer to the default escaper.
if (unicodeScalar == 0x1F603) { return false; } // does not require escaping
else { return JavaScriptEncoder.Default.WillEncode(unicodeScalar); }
}
public override unsafe bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten)
{
// For anything that needs to be escaped, defer to the default escaper.
return JavaScriptEncoder.Default.TryEncodeUnicodeScalar(unicodeScalar, buffer, bufferLength, out numberOfCharactersWritten);
}
}
}
可他这里需要重写TextEncoder
的FindFirstCharacterToEncode
和TryEncodeUnicodeScalar
,可对于团队项目来说,unsafe
并不是能随意开关的.
在#54193
题主也提供了自己的兼容方案,但其较为复杂,不仅使用了unsafe
还额外引用了emoji之类的编码Map
如果能接受unsafe
,GrabYourPitchforks
的方案也是算是不错的,当然,对于我们真正需要的格式还是调整一下.
若不能接受其调整的范围,按照目前我查阅到的信息,对于Core的迁移还是直接使用Newtonsoft.Json
来兼容旧版内容会更好
2).Core使用Newtonsoft.Json
在Nuget安装Microsoft.AspNetCore.Mvc.NewtonsoftJson
因为我这边项目版本是Core 3.1
,所以需要使用3.1.18
版本
Startup.cs
配置:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" });
});
}
PostMan效果:
Q.E.D.