如何使用Circe进行动态解码?

旺特

我的问题有点棘手。我有一个这样的案例课

case class Foo(
    id: String,
    name: String,
    field1: Boolean,
    field2: Boolean,
    field3: Boolean,
    field4: Boolean
)

但是,我有两种输入类型,一种非常适合case类Foo另一种是缺失值field3field4,看起来像{id: "Test", name: "Test", field1: true, field2: true},我想创建一个Decoder[Foo],对于这两种情况下工作,如果输入丢失field3field4,只是设置的默认值false那可能吗?

例如,

(1)对于输入{id: "Test", name: "Test", field1: true, field2: true},我想将其解码为

Foo("Test, "Test", true, true, false, flase)

(2)对于输入{id: "Test", name: "Test", field1: true, field2: true, field3: true, field4: false},我想将其解码为

Foo("Test, "Test", true, true, true, flase)

我知道最好的解决方案是将setfield3field4as设置Option[Boolean],但是我们要按照原始设计来实现大量代码,而更改数据模型将引入很多代码更改。因此,只想看看是否有任何临时解决方案。

非常感谢你!

奥列格(Oleg Pyzhcov)

有多种方法可以做到这一点。我将假设您不会从头开始构建编解码器,而不会使用已经存在的东西。

默认参数+通用扩展名

有一个circe-generic-extras程序包,它允许对自动派生的编解码器进行一些自定义。特别是,它确实允许您使用默认参数作为后备值。

缺点是编译速度较慢,并且还要求您io.circe.generic.extras.Configuration在范围上隐式


因此,首先您需要该隐式配置:

object Configs {
  implicit val useDefaultValues = Configuration.default.withDefaults
}

这通常会放入项目中的一些通用util包中,因此您可以轻松地重用这些配置。

然后,您可以@ConfiguredJsonCodec在类上使用宏注释,或extras.semiauto.deriveConfiguredCodec在其伴随类中使用

import Configs.useDefaultValues

@ConfiguredJsonCodec
case class Foo(
    id: String,
    name: String,
    field1: Boolean,
    field2: Boolean,
    field3: Boolean = false,
    field4: Boolean = false
)

重要的是不要忘记一次配置导入,并且不要一次导入多个配置。否则,您会得到一个无用的错误

could not find Lazy implicit value of type io.circe.generic.extras.codec.ConfiguredAsObjectCodec[Foo]

Foo现在,如果缺少具有默认值的字段,则足以解码

println {
    io.circe.parser.decode[Foo]("""
{
  "id": "someid",
  "name": "Gordon Freeman",
  "field1": false,
  "field2": true
}
""")
  }

这里的设备齐全的斯卡斯蒂

后备解码器

想法如下:拥有一个单独的case类来描述数据的旧格式,并构建一个解码器以尝试将数据解析为旧格式和新格式。Circe解码器仅具有or用于此类尝试的组合


在这里,首先描述数据的“旧”格式,以及将其升级到新格式的方法:

@JsonCodec(decodeOnly = true)
case class LegacyFoo(
    id: String,
    name: String,
    field1: Boolean,
    field2: Boolean,
) {
  def upgrade: Foo =
    Foo(id, name, field1, field2, false, false)
}

使用新格式时,您必须手动加入编解码器,因此不能使用宏注释。不过,您可以使用generic.semiauto.deriveXXX方法来不必自己列出所有字段:

case class Foo(
    id: String,
    name: String,
    field1: Boolean,
    field2: Boolean,
    field3: Boolean,
    field4: Boolean
)

object Foo {
  implicit val encoder: Encoder[Foo] = semiauto.deriveEncoder[Foo]
  implicit val decoder: Decoder[Foo] =
    semiauto.deriveDecoder[Foo] or Decoder[LegacyFoo].map(_.upgrade)
}

对于相同的负载,这也将“正常工作”:

println {
    io.circe.parser.decode[Foo]("""
{
  "id": "someid",
  "name": "Gordon Freeman",
  "field1": false,
  "field2": true
}
""")
}

斯卡斯蒂在这里


第一种方法需要一个额外的库,但模板较少。它还将允许调用方提供(例如field4但不能提供)field3-在第二种方法中,该值field4将在那种情况下被完全丢弃。

与“添加默认值的字段”相比,第二个方法允许处理更复杂的更改,例如从多个其他值中计算值或更改集合中的结构,还可以在以后需要时提供更多版本。

哦,如果您不想公开额外的公共数据类型,也可以放入,并将其LegacyFoo设为object Foo私有。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

使用 Swift Decodable 进行动态(但称为另一个键值)JSON 解码

来自分类Dev

如何使用Three.js进行动态CSG操作

来自分类Dev

如何使用pg_shard进行动态缩放

来自分类Dev

如何使用pg_shard进行动态缩放

来自分类Dev

使用.htaccess进行动态URL重写

来自分类Dev

使用Linq进行动态过滤

来自分类Dev

使用NGINX进行动态路由

来自分类Dev

使用$ parse进行动态数组推送

来自分类Dev

使用Vuetify进行动态计算

来自分类Dev

使用NodeJS进行动态对象遍历

来自分类Dev

使用Linq进行动态过滤

来自分类Dev

使用.htaccess进行动态URL重写

来自分类Dev

使用Maven进行动态Web项目

来自分类Dev

使用NGINX进行动态路由

来自分类Dev

使用Visual Studio进行动态调试

来自分类Dev

使用 char 进行动态分配

来自分类Dev

使用宏进行动态版本选择

来自分类Dev

进行动态编程

来自分类Dev

如何避免在IN子句中使用多个IF语句进行动态查询

来自分类Dev

如何使用jekyll和javascript / jquery进行动态模态弹出

来自分类Dev

如何使用键集分页对表数据进行动态排序?

来自分类Dev

如何使用固定的熊猫数据框进行动态matplotlib绘图?

来自分类Dev

如何在vue.js中使用Ajax进行动态下拉

来自分类Dev

使用 UnityContainer 进行动态 IOC 映射 - 我该如何实现?

来自分类Dev

如何在MySQL中进行动态限制?

来自分类Dev

如何从Laravel中的数组值进行动态链接?

来自分类Dev

如何从共享生成静态库(.a)以进行动态链接

来自分类Dev

如何进行动态的RESTful服务调用?

来自分类Dev

如何在数据能力中进行动态路由