使用 circe 将 Scala None 编码为 JSON 值
Encoding Scala None to JSON value using circe
假设我有以下情况 类 需要使用 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
}
使用 Printer
的 dropNullKeys
config, e.g. b.asJson.noSpaces.copy(dropNullKeys = true)
would result in omitting None
s from output whereas setting it to false
would encode None
s as null
()。但是如何在每个字段的基础上控制此设置?
执行此操作的最佳方法可能只是为 B
:
的 semi-automatically 派生编码器添加 post-processing 步骤
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 对象中删除您想要的任何 null-valued 字段(这里我只是删除 B
中的 b1
).
值得注意的是,目前您不能将 @JsonCodec
注释与伴生对象中的显式定义实例结合使用。这不是注释的固有限制——我们可以在宏扩展期间检查伴生对象的 "overriding" 个实例,但这样做会使实现变得更加复杂(现在很简单)。解决方法非常简单(只需显式使用 deriveDecoder
),但我们当然很乐意考虑请求支持混合和匹配 @JsonCodec
和显式实例的问题。
Circe 在 Json
上添加了一个方法 dropNullValues
,它使用了上面提到的 Travis Brown。
def dropNulls[A](encoder: Encoder[A]): Encoder[A] =
encoder.mapJson(_.dropNullValues)
implicit val entityEncoder: Encoder[Entity] = dropNulls(deriveEncoder)
假设我有以下情况 类 需要使用 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
}
使用 Printer
的 dropNullKeys
config, e.g. b.asJson.noSpaces.copy(dropNullKeys = true)
would result in omitting None
s from output whereas setting it to false
would encode None
s as null
(
执行此操作的最佳方法可能只是为 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 对象中删除您想要的任何 null-valued 字段(这里我只是删除 B
中的 b1
).
值得注意的是,目前您不能将 @JsonCodec
注释与伴生对象中的显式定义实例结合使用。这不是注释的固有限制——我们可以在宏扩展期间检查伴生对象的 "overriding" 个实例,但这样做会使实现变得更加复杂(现在很简单)。解决方法非常简单(只需显式使用 deriveDecoder
),但我们当然很乐意考虑请求支持混合和匹配 @JsonCodec
和显式实例的问题。
Circe 在 Json
上添加了一个方法 dropNullValues
,它使用了上面提到的 Travis Brown。
def dropNulls[A](encoder: Encoder[A]): Encoder[A] =
encoder.mapJson(_.dropNullValues)
implicit val entityEncoder: Encoder[Entity] = dropNulls(deriveEncoder)