使用自定义 JsonConverter 解决 long 类型在前端的精度问题

问题

Javascript 的 number 类型存在精度限制,浏览器反序列化 JSON 时,无法完整保留 long 类型的精度。

在 JSON 序列化时将 long 转换为 string 进行传递就可以保留精度。在 ASP.NET Core 中,可以创建一个 自定义 JsonConverter 来达到这一目的。

实现 longstring 转换的 JsonConverter

创建一个类 LongToStringJsonConverter,示例代码(来自:https://github.com/cnblogs/Architecture/blob/main/src/Cnblogs.Architecture.Ddd.Cqrs.AspNetCore/LongToStringConverter.cs)

cs 复制代码
/// <summary>
/// JSON 序列化时,在 long 和 string 类型之间转换。
/// </summary>
public class LongToStringConverter : JsonConverter<long>
{
    /// <inheritdoc />
    public override long Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType != JsonTokenType.String)
        {
            return reader.GetInt64();
        }

        var raw = reader.GetString();
        if (string.IsNullOrWhiteSpace(raw))
        {
            throw new JsonException("string is empty");
        }

        var success = long.TryParse(raw, out var parsed);
        if (success == false)
        {
            throw new JsonException("string value can't be converted to long");
        }

        return parsed;
    }

    /// <inheritdoc />
    public override void Write(Utf8JsonWriter writer, long value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString());
    }
}

然后可以在应用代码里启用将刚刚写好的自定义 JsonConverter,可以根据项目的不同需求,指定启用的范围。

仅对类中的某一字段启用:

cs 复制代码
public class TestDto
{
    [JsonConverter(typeof(LongToStringJsonConverter))]
    public long Id { get; set; }
}

对整个 API 项目全局启用:

新的 Minimal API 项目

cs 复制代码
// Program.cs

builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.Converters.Add(new LongToStringJsonConverter()));

传统的 MVC 项目

cs 复制代码
builder.Services.AddControllers().AddJsonOptions(options => options.SerializerOptions.Converters.Add(new LongToStringJsonConverter()))

在前端,如果使用 Typescript 的话,需要将对应类型从 number 改为 string 以实现正确的类型推断。

typescript 复制代码
interface TestDto {
  // id: number;
  id: string;
}