在本系列的上一篇文章中,我们注意到强类型ID的实体,序列化为 JSON 的时候报错了,就像这样:

{
"id": {
"value": 1
},
"name": "Apple",
"unitPrice": 0.8
}

不过想了一下,这样的意外也是在意料之中的,强类型ID是record类型,而不是原始类型,因此将其序列化为一个对象是有意义的,但这显然不是我们想要的……让我们看看如何解决这个问题。

System.Text.Json

在最新版本的ASP.NET Core(从3.0)中,默认的JSON序列化程序是System.Text.Json,因此让我首先介绍这种。

为了将强类型的id序列化为其值而不是对象,我们需要编写一个通用的 JsonConverter:

public class StronglyTypedIdJsonConverter<TStronglyTypedId, TValue> : JsonConverter<TStronglyTypedId>
where TStronglyTypedId : StronglyTypedId<TValue>
where TValue : notnull
{
public override TStronglyTypedId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType is JsonTokenType.Null)
return null; var value = JsonSerializer.Deserialize<TValue>(ref reader, options);
var factory = StronglyTypedIdHelper.GetFactory<TValue>(typeToConvert);
return (TStronglyTypedId)factory(value);
} public override void Write(Utf8JsonWriter writer, TStronglyTypedId value, JsonSerializerOptions options)
{
if (value is null)
writer.WriteNullValue();
else
JsonSerializer.Serialize(writer, value.Value, options);
}
}

逻辑很简单,对于直接读取 id.value, 对于反序列化,创建一个强类型id的实例,然后给它赋值。

然后在启动类中配置:

services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(
new StronglyTypedIdJsonConverter<ProductId, int>());
});

现在拿到了想要的结果:

{
"id": 1,
"name": "Apple",
"unitPrice": 0.8
}

真好!不过,还有有一个问题:我们只为添加了一个对于ProductId的转换器,但我不想为每种类型的强类型ID添加另一个转换器!我们想要一个适用于所有强类型id的转换器……,现在可以创建一个转换器工厂(ConverterFactory),就像下边这样:

public class StronglyTypedIdJsonConverterFactory : JsonConverterFactory
{
private static readonly ConcurrentDictionary<Type, JsonConverter> Cache = new(); public override bool CanConvert(Type typeToConvert)
{
return StronglyTypedIdHelper.IsStronglyTypedId(typeToConvert);
} public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
return Cache.GetOrAdd(typeToConvert, CreateConverter);
} private static JsonConverter CreateConverter(Type typeToConvert)
{
if (!StronglyTypedIdHelper.IsStronglyTypedId(typeToConvert, out var valueType))
throw new InvalidOperationException($"Cannot create converter for '{typeToConvert}'"); var type = typeof(StronglyTypedIdJsonConverter<,>).MakeGenericType(typeToConvert, valueType);
return (JsonConverter)Activator.CreateInstance(type);
}
}

首先我们查看需要转换的类型,检查它是否实际上是强类型的id,然后为该类型创建特定转换器的实例,我们添加了一些缓存,避免每次都进行反射工作。

现在,我们没有添加特定的JsonConvert,只是添加了一个Factory,然后在启动文件修改,现在,我们的转换器将应用于每个强类型ID

services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(
new StronglyTypedIdJsonConverterFactory());
});

Newtonsoft.Json

如果您的项目使用的是Newtonsoft.Json进行JSON序列化,那就很简单了。

当它序列化一个值时,Newtonsoft.Json 查找一个compatible JsonConverter,如果找不到,就查找一个TypeConverter, 如果TypeConverter存在,并且可以将值转换为string,那么它把值序列化为字符串, 因为我们之前定义了 TypeConverter,Newtonsoft.Json查找到了,我得到以下结果:

{
"id": "1",
"name": "Apple",
"unitPrice": 0.8
}

几乎是正确的……除了id值不应序列化为字符串,而应序列化为数字,如果id值是GUID或字符串而不是int,那就很好,则需要编写一个自定义转换器。

它和 System.Text.Json 的转换器非常相似,不同之处在于Newtonsoft.Json没有转换器工厂(ConvertFactory)的概念,相反,我们将编写一个非泛型转换器:

public class StronglyTypedIdNewtonsoftJsonConverter : JsonConverter
{
private static readonly ConcurrentDictionary<Type, JsonConverter> Cache = new(); public override bool CanConvert(Type objectType)
{
return StronglyTypedIdHelper.IsStronglyTypedId(objectType);
} public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var converter = GetConverter(objectType);
return converter.ReadJson(reader, objectType, existingValue, serializer);
} public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is null)
{
writer.WriteNull();
}
else
{
var converter = GetConverter(value.GetType());
converter.WriteJson(writer, value, serializer);
}
} private static JsonConverter GetConverter(Type objectType)
{
return Cache.GetOrAdd(objectType, CreateConverter);
} private static JsonConverter CreateConverter(Type objectType)
{
if (!StronglyTypedIdHelper.IsStronglyTypedId(objectType, out var valueType))
throw new InvalidOperationException($"Cannot create converter for '{objectType}'"); var type = typeof(StronglyTypedIdNewtonsoftJsonConverter<,>).MakeGenericType(objectType, valueType);
return (JsonConverter)Activator.CreateInstance(type);
}
} public class StronglyTypedIdNewtonsoftJsonConverter<TStronglyTypedId, TValue> : JsonConverter<TStronglyTypedId>
where TStronglyTypedId : StronglyTypedId<TValue>
where TValue : notnull
{
public override TStronglyTypedId ReadJson(JsonReader reader, Type objectType, TStronglyTypedId existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType is JsonToken.Null)
return null; var value = serializer.Deserialize<TValue>(reader);
var factory = StronglyTypedIdHelper.GetFactory<TValue>(objectType);
return (TStronglyTypedId)factory(value);
} public override void WriteJson(JsonWriter writer, TStronglyTypedId value, JsonSerializer serializer)
{
if (value is null)
writer.WriteNull();
else
writer.WriteValue(value.Value);
}
}

然后在启动文件中这样设置:

services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.Converters.Add(
new StronglyTypedIdNewtonsoftJsonConverter());
});

然后,我们得到了预期的结果,输出的结果是这样:

{
"id": 1,
"name": "Apple",
"unitPrice": 0.8
}

原文作者: thomas levesque

原文链接:https://thomaslevesque.com/2020/12/07/csharp-9-records-as-strongly-typed-ids-part-3-json-serialization/

最后

欢迎扫码关注我们的公众号 【全球技术精选】,专注国外优秀博客的翻译和开源项目分享,也可以添加QQ群 897216102

使用 C# 9 的records作为强类型ID - JSON序列化的更多相关文章

  1. 使用 C# 9 的records作为强类型ID - 初次使用

    强类型ID 实体通常是整数,GUID或者string类型,因为数据库直接支持这些类型,但是,如果实体的ID的类型是一样的,比如都是整数的ID,这有可能会出现ID值传错的问题,看下边的示例. publi ...

  2. 使用 C# 9 的records作为强类型ID - 路由和查询参数

    上一篇文章,我介绍了使用 C# 9 的record类型作为强类型id,非常简洁 public record ProductId(int Value); 但是在强类型id真正可用之前,还有一些问题需要解 ...

  3. Spring+Mybatis 复杂的分组查询

    1.需要的结果数据格式为 { "responseCode": "0000", "responseMsg": null, "data ...

  4. python---CRM用户关系管理

    Day1:项目分析 一:需求分析 二:CRM角色功能介绍 三:业务场景分析 销售: .销售A 从百度推广获取了一个客户,录入了CRM系统,咨询了Python课程,但是没有报名 .销售B 从qq群获取一 ...

  5. 使用强类型实体Id来避免原始类型困扰(一)

    原文地址:https://andrewlock.net/using-strongly-typed-entity-ids-to-avoid-primitive-obsession-part-1/ 作者: ...

  6. 通过ajax 后台传递的 区域id 选中ztree的节点 并展开节点

    代码如下: < script type = "text/javascript" >    var flag = "<%=request.getParam ...

  7. Elasticsearch由浅入深(三)document的核心元数据、Id、_source元数据、全量替换、强制创建以及删除机制

    document的核心元数据 document的核心元数据有三个:_index._type._id 初始化数据: PUT test_index/test_type/ { "test_cont ...

  8. 完美解决方案-雪花算法ID到前端之后精度丢失问题

    最近公司的一个项目组要把以前的单体应用进行为服务拆分,表的ID主键使用Mybatis plus默认 的雪花算法来生成. 快下班的时候,小伙伴跑过来找我,:"快给我看看这问题,卡这卡了小半天了 ...

  9. .NET缓存框架CacheManager在混合式开发框架中的应用(1)-CacheManager的介绍和使用

    在我们开发的很多分布式项目里面(如基于WCF服务.Web API服务方式),由于数据提供涉及到数据库的相关操作,如果客户端的并发数量超过一定的数量,那么数据库的请求处理则以爆发式增长,如果数据库服务器 ...

随机推荐

  1. 恋爱话术库撩妹至尊VIP版

    本软件来自互联网,解锁永久至尊VIP 是一款教你撩妹密语软件.和女生聊天没有话题? 不知道怎么逗乐女生? 女生生气了不会哄? 不知道怎么让女生愿意跟你聊下去? 不知道女生对你有没有意思? 遇到不知道怎 ...

  2. 20201204-3 opp编程好处

    面向对象编程(Object-Oriented Programming )介绍 对于编程语言的初学者来讲, OOP不是一个很容易理解的编程方式,大家虽然都按老师讲的都知道0OP的三大特性是 继承.封装. ...

  3. mysql主从同步错误

    一.主从同步报错 mysql> show slave status\G; *************************** 1. row ************************* ...

  4. chrome 开发者工具使用一例

    今天搜到了一篇我想看的文章,某网站上又是弹出注册小窗遮挡,又是一堆漂浮广告,还把字体搞成灰色. 右键审查元素,找到几个div,删掉:原来那个字体的灰色,是个什么script做的遮罩,也删掉. 然后整个 ...

  5. [日常摸鱼]Luogu2878 [USACO07JAN]Protecting the Flowers

    直接贴题面x 有$n$头奶牛跑到FJ的花园里去吃花儿了,它们分别在距离牛圈$T$分钟处吃花儿,每分钟会吃掉$D$朵卡哇伊的花儿,FJ现在要将它们给弄回牛圈,但是他每次只能弄一头回去,来回用时总共为$2 ...

  6. Web服务器-服务器开发-返回固定页面的HTTP服务器(3.3.1)

    @ 目录 1.注意 2.代码 关于作者 1.注意 浏览器解析的时候偶\r\n才算一个换行符 发送的str要编码,这里使用的是utf8 其他的都和上一篇没有什么区别 这里主要返回的是固定的网址 2.代码 ...

  7. 怎样用Java 8优雅的开发业务

    怎样用Java 8优雅的开发业务 目录 怎样用Java 8优雅的开发业务 函数式编程 流式编程 基本原理 案例 优雅的空处理 新的并发工具类CompletableFuture 单机批处理多线程执行模型 ...

  8. maven继承父工程统一版本号

    一.建立一个maven工程 pom类型 统一管理依赖以及版本号 子工程不会使用所有的定义的依赖 子工程使用依赖时无需指定版本号 pom.xml <project xmlns="http ...

  9. 如何用Python判断一个文件是否被占用?

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 今天有同学问,用os模块的access()能否判断一个文件是否被占用?直觉上,这是行不通的,因为ac ...

  10. Python炫技操作:五种Python 转义表示法

    1. 为什么要有转义? ASCII 表中一共有 128 个字符.这里面有我们非常熟悉的字母.数字.标点符号,这些都可以从我们的键盘中输出.除此之外,还有一些非常特殊的字符,这些字符,我通常很难用键盘上 ...