gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 20:12:49 25 4
gpt4 key购买 nike

我的问题有点棘手。我有一个看起来像这样的案例类

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

但是,我有两种类型的输入,一种非常适合案例类 Foo .另一个是 field3 的缺失值和 field4 , 看起来像 {id: "Test", name: "Test", field1: true, field2: true} , 我想创建一个 Decoder[Foo]如果输入丢失 field3,则适用于这两种情况和 field4 , 只需设置一个默认值 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)

我知道最好的解决方案是设置 field3field4Option[Boolean] ,但是我们按照原始设计实现了大量代码,并且更改数据模型将引入大量代码更改。所以只想看看有没有make shift解决方案。

非常感谢!

最佳答案

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

默认参数 + 通用附加项

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
}
""")
}

自成体系 here .

后备解码器

想法如下:有一个单独的案例类来描述旧格式的数据,并构建一个解码器来尝试将数据解析为旧格式和新格式。 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
}
""")
}

斯卡斯蒂 here .

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

第二个允许处理比“添加了默认值的字段”更复杂的更改,例如计算其他几个值或更改集合内的结构,并且如果您以后需要它们,还可以拥有更多版本。

哦,你也可以放 LegacyFoo进入 object Foo , 如果您不想暴露额外的公共(public)数据类型,请将其设为私有(private)。

关于scala - 如何使用 Circe 进行动态解码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59150232/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com