使用circe将Scala无编码为JSON值

msi*_*ilb 8 json scala circe

假设我有以下需要使用circe序列化为JSON对象的case类:

@JsonCodec
case class A(a1: String, a2: Option[String])

@JsonCodec
case class B(b1: Option[A], b2: Option[A], b3: Int)
Run Code Online (Sandbox Code Playgroud)

现在我需要编码val b = B(None, Some(A("a", Some("aa")), 5)为JSON但我希望能够控制它是否输出为

{
  "b1": null,
  "b2": {
          "a1": "a",
          "a2": "aa"
        },
  "b3": 5
}
Run Code Online (Sandbox Code Playgroud)

要么

{
  "b2": {
          "a1": "a",
          "a2": "aa"
        },
  "b3": 5
}
Run Code Online (Sandbox Code Playgroud)

使用Printer's dropNullKeysconfig,例如,b.asJson.noSpaces.copy(dropNullKeys = true)将导致None从输出中省略s,而将其设置为falseNones 编码为null(也参见此问题).但是如何在每个字段的基础上控制此设置?

Tra*_*own 15

执行此操作的最佳方法可能只是为半自动派生编码器添加后处理步骤B:

import io.circe.{ Decoder, JsonObject, ObjectEncoder }
import io.circe.generic.JsonCodec
import io.circe.generic.semiauto.{ deriveDecoder, deriveEncoder }

@JsonCodec
case class A(a1: String, a2: Option[String])
case class B(b1: Option[A], b2: Option[A], b3: Int)

object B {
  implicit val decodeB: Decoder[B] = deriveDecoder[B]
  implicit val encodeB: ObjectEncoder[B] = deriveEncoder[B].mapJsonObject(
    _.filter {
      case ("b1", value) => !value.isNull
      case _ => true
    }
  )
}
Run Code Online (Sandbox Code Playgroud)

然后:

scala> import io.circe.syntax._
import io.circe.syntax._

scala> B(None, None, 1).asJson.noSpaces
res0: String = {"b2":null,"b3":1}
Run Code Online (Sandbox Code Playgroud)

您可以调整参数过滤器删除您从JSON对象要(在这里我只是删除取其空值字段b1B).

值得注意的是,目前您无法@JsonCodec在注册对象中组合注释和显式定义的实例.这不是注释的固有限制 - 我们可以在宏扩展期间检查伴随对象"覆盖"实例,但这样做会使实现变得更加复杂(现在它非常简单).解决方法非常简单(只是deriveDecoder明确使用),但当然我们很乐意考虑一个请求支持混合和匹配@JsonCodec以及显式实例的问题.