Я пытаюсь маршалировать и демаршалировать поле 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
(не отображать поля со значениями None).
Затем я заменил формат на 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))
}
Теперь компилятор доволен, и тест проходит.
Но теперь мне нужно реализовать отдельный Reads. Если бы я мог, я бы предпочел этого не делать, поскольку это нарушает принцип DRY.
Что я делаю неправильно, и когда write[Option[...]] работает отлично, почему бы не format[Option[...]]?