使用 circe 将 Scala None 编码为 JSON 值

Posted

技术标签:

【中文标题】使用 circe 将 Scala None 编码为 JSON 值【英文标题】:Encoding Scala None to JSON value using circe 【发布时间】:2017-07-10 13:15:28 【问题描述】:

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

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

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

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


  "b1": null,
  "b2": 
          "a1": "a",
          "a2": "aa"
        ,
  "b3": 5


  "b2": 
          "a1": "a",
          "a2": "aa"
        ,
  "b3": 5

使用PrinterdropNullKeys 配置,例如b.asJson.noSpaces.copy(dropNullKeys = true) 将导致从输出中省略 Nones,而将其设置为 false 会将 Nones 编码为 null (see also this question)。但是如何才能在每个字段的基础上控制此设置呢?

【问题讨论】:

【参考方案1】:

做到这一点的最佳方法可能只是为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
    
  )

然后:

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

scala> B(None, None, 1).asJson.noSpaces
res0: String = "b2":null,"b3":1

您可以调整过滤器的参数以从 JSON 对象中删除您想要的任何空值字段(这里我只是删除 B 中的 b1)。

值得注意的是,目前您不能将@JsonCodec 注释与伴生对象中显式定义的实例结合起来。这不是注释的固有限制——我们可以在宏扩展期间检查伴随对象的“覆盖”实例,但这样做会使实现变得更加复杂(现在它很简单)。解决方法非常简单(只需显式使用deriveDecoder),但我们当然很乐意考虑请求支持混合和匹配@JsonCodec 和显式实例的问题。

【讨论】:

谢谢,这非常适合用例。实际上,我真正需要的是一种让库的用户能够控制该值是从 json 中“省略”还是序列化为 null 的方法。我想我唯一的选择是创建某种 ADT 来映射出 3 个可能的输出:None(省略)、Some(ANullValue)Some(A(...)),然后按照您的建议使用自定义编码器。 @msilb 您可以在哪里为您的用例提供自定义编码器?我很乐意看到并举例。【参考方案2】:

Circe 在 Json 上添加了一个方法 dropNullValues,它使用了上面提到的 Travis Brown。

def dropNulls[A](encoder: Encoder[A]): Encoder[A] =
    encoder.mapJson(_.dropNullValues)

implicit val entityEncoder: Encoder[Entity] = dropNulls(deriveEncoder)

【讨论】:

以上是关于使用 circe 将 Scala None 编码为 JSON 值的主要内容,如果未能解决你的问题,请参考以下文章

Circe Scala - 编码和解码 Map[] 和案例类

带有案例类的抽象类的scala circe编码器/解码器

使用 circe 在 Scala 中 JSON 将嵌套字段解码为 Map[String, String]

使用 circe 时如何在 Scala 中表示动态 JSON 键

如何在circe中编码/解码json的时间戳?

如何使用 circe 将密封特征案例对象转换为字符串