我的问题有点棘手。我有一个这样的案例课
case class Foo(
id: String,
name: String,
field1: Boolean,
field2: Boolean,
field3: Boolean,
field4: Boolean
)
但是,我有两种输入类型,一种非常适合case类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)
我知道最好的解决方案是将setfield3
和field4
as设置为Option[Boolean]
,但是我们要按照原始设计来实现大量代码,而更改数据模型将引入很多代码更改。因此,只想看看是否有任何临时解决方案。
非常感谢你!
有多种方法可以做到这一点。我将假设您不会从头开始构建编解码器,而不会使用已经存在的东西。
有一个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] 删除。
我来说两句