简体   繁体   中英

Using Reads, Writes and Formats in Play framework

I have some models in a Play! application that I would like to serialize/deserialize to and from JSON. I used to have separate methods for that, but I have seen that the preferred way is to give an implicit instance of Formats[T] or Reads[T] , like

import play.api.libs.json.{ JsValue, Reads } 

case class Foo(bar: Int, ...)

object Foo {
  implicit object FooReads extends Reads[Foo] {
    def reads(json: JsValue): Foo = //whatever
  }
}

Now, it may happen that the model has the correct fields in the JSON, but it does not validate. In this case, I am not able to deserialize - I should get an exception when using json.as[Foo] or None when using json.asOpt[Foo] .

If I throw an exception when I find a field that does not validate, everything seems to work as expected. But I took the care of trying to find out what exception I should throw, and in the source for JsValue I found this

def asOpt[T](implicit fjs: Reads[T]): Option[T] = fjs.reads(this).fold(
    valid = v => Some(v),
    invalid = _ => None
  ).filter {
  case JsUndefined(_) => false
  case _ => true
}

Now, I cannot understand how this is supposed to work. The implicit instance of fjs is supplied by myself in the companion object, so I know that fjs.reads(this) either returns a Foo or throws an exception.

Where is this fold coming from? It certainly is not a method on Foo . I guess one could have an implicit conversion, but it should be from Any to something with a fold method, so it could not be of much interest. Even worse, if fjs.reads(this) throws an exception, there is nothing to catch it!

So, how should one handle invalid input in the JSON in an instance of Reads[T] ? And how does the mechanism above actually work?

Looking at JsonValue.scala in Play 2.0.x:

def asOpt[T](implicit fjs: Reads[T]): Option[T] = catching(classOf[RuntimeException]).opt(fjs.reads(this)).filter {
  case JsUndefined(_) => false
  case _ => true
}

In fact the code is using scala.util.control.Exception.catching[T](exceptions: Class[_]*): Catch[T] , which returns a Catch[T] . Then it calls opt(...) on it. If an exception is thrown, then it will return a None instead of an instance of T .

So, when you get an error deserializing, you can safely throw an exception.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM