选项[String]没有Json格式化程序?

11

我正在尝试将一个 Option[String] 字段从 JSON 序列化和反序列化。在我的用例中,None 值应该序列化为 "null"。这是我目前的代码:

import org.scalatest.{FlatSpec, Matchers}

import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._


case class Person(
  id: Int,
  firstName: Option[String],
  lastName: Option[String]
)

object Person {
  implicit lazy val personFormat = (
    (__ \ "id").format[Int] and
    (__ \ "first_name").format[Option[String]] and
    (__ \ "last_name").format[Option[String]]
  )(Person.apply, unlift(Person.unapply))
}

class PersonSpec extends FlatSpec with Matchers {
  "When Person instance is marshaled None fields " should
    "be serialized as \"null\" values" in {
    val person = Person(1, None, None)
    import Person._
    val json = Json.toJson(person)
    println(json)
    (json \ "id").as[Int] should be (1)
    (json \ "first_name").get should be (JsNull)
    (json \ "last_name").get should be (JsNull)
  }
}

这导致以下编译器错误:

PersonSpec.scala:19: No Json formatter found for type Option[String]. Try to implement an implicit Format for this type.
[error]     (__ \ "first_name").format[Option[String]] and
[error]                               ^
这是我尝试过的一些方法:
(__ \ "first_name").format[Option[String]]替换为(__ \ "first_name").formatNullable[String]可以使编译器满意,但测试失败(""java.util.NoSuchElementException: None.get ""),以下是来自println(json)的输出。
{"id":1}

这证实了 formatNullable 的行为(不渲染空值字段)。

接下来,我用 writes 替换了格式。就像这样:

object Person {
  implicit lazy val personWrite = (
    (__ \ "id").write[Int] and
    (__ \ "first_name").write[Option[String]] and
    (__ \ "last_name").write[Option[String]]
  )(unlift(Person.unapply))
}

现在,编译器很高兴并且测试通过。

但是现在我需要实现一个单独的“读取”功能。如果可能的话,我宁愿不这样做,因为它违反了DRY原则。

我到底做错了什么,为什么write[Option[...]]运行良好而format[Option[...]]却不行?

2个回答

16
将此代码添加到您的PersonFormat中,以使其隐式可见即可使其正常工作。
implicit def optionFormat[T: Format]: Format[Option[T]] = new Format[Option[T]]{
    override def reads(json: JsValue): JsResult[Option[T]] = json.validateOpt[T]

    override def writes(o: Option[T]): JsValue = o match {
      case Some(t) ⇒ implicitly[Writes[T]].writes(t)
      case NoneJsNull
    }
  }

我认为在游戏中,应该将选项值字段视为可选的,因此您观察到了 formatNullable 的行为。请注意保留 HTML 标签。

7

您可以使用:

(__ \ "first_name").formatNullable[String]

正是我需要阅读的 Optional[String],谢谢! - calloc_org

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接