【发布时间】:2018-01-29 22:09:40
【问题描述】:
我需要解析一个 JSON 对象,使用 play-json 并区分缺失值、字符串值和空值。
例如,我可能想反序列化为以下案例类:
case class MyCaseClass(
a: Option[Option[String]]
)
'a' 的值是什么意思:
- 无 - 缺少“a” - 正常的 play-json 行为
- Some(Some(String)) - "a" 有一个字符串值
- Some(None) - “a”有一个空值
因此,预期行为的示例如下:
{}
should deserialize to myCaseClass(None)
{
"a": null
}
should deserialize as myCaseClass(Some(None))
{
"a": "a"
}
should deserialize as myCaseClass(Some(Some("a"))
我尝试过编写自定义格式化程序,但是 formatNullable 和 formatNullableWithDefault 方法不区分缺失值和空值,因此我在下面编写的代码无法生成 Some(None) 结果
object myCaseClass {
implicit val aFormat: Format[Option[String]] = new Format[Option[String]] {
override def reads(json: JsValue): JsResult[Option[String]] = {
json match {
case JsNull => JsSuccess(None) // this is never reached
case JsString(value) => JsSuccess(Some(value))
case _ => throw new RuntimeException("unexpected type")
}
}
override def writes(codename: Option[String]): JsValue = {
codename match {
case None => JsNull
case Some(value) => JsString(value)
}
}
}
implicit val format = (
(__ \ "a").formatNullableWithDefault[Option[String]](None)
)(MyCaseClass.apply, unlift(MyCaseClass.unapply))
}
我在这里错过了一个技巧吗?我该怎么办?除了 Option[Option[Sting]] 之外,我非常愿意以其他方式对最终值进行编码,例如某种封装这种情况的类:
case class MyContainer(newValue: Option[String], wasProvided: Boolean)
【问题讨论】:
-
而且,是的 - 我知道我可以为整个对象编写一个完全自定义的读取方法。我试图避免这种情况。
-
类型
Option[Option[_]]无论如何都很难理解。顺便说一句,我看不到这种null/missing 区别的好处。 -
@cchantep .. 肯定有一点代码味道 - 但这些是我的要求。有一个看似合理的用例。无论如何,它的 json 解析是我正在努力解决的问题。出于这个问题的目的,如果我们认为 undefined 和 null 是 json 中的单独值......我如何在不自己遍历 json 的情况下解析它。
-
您假设 undefined 或 null 是不同的,但它们都使用不同的格式:表示指定字段没有值的事实
-
我绝对认为
nullvs 省略是两个完全不同的意图。不幸的是,在 JSON 规范中对此的歧义并没有减少,但直觉上,我认为执行@iandotkelly 在这里提出的建议是有意义的(尤其是在考虑 PATCH 时)。我在这里与核心 play-json 团队讨论了这个问题:discuss.lightbend.com/t/…