简体   繁体   中英

Play JSON Combinators

In Play 2.1 we use something like below to get a Creature Object out of a JSON through reads.

implicit val creatureReads = (
  (__ \ "name").read[String] and
  (__ \ "isDead").read[Boolean] and
  (__ \ "weight").read[Float]
)(Creature.apply _)

Being relative new in Scala, I'm trying to understand if there is any other way to build the Creature object without using the Apply method? Would it be possible to have an anonymous function to create the object instead of relying on the apply?

I have use cases where most of the fields in my objects could be missing, but I would like to still build the object out of what I have. Is it better to just define one READ for the object and use readnullable for each of the fields?

I also could have complex conditionals, so would it be cleaner to just define custom functions to build it instead of trying to capture all cases in one Reader?

Yes of course, the apply method is just a method that takes all the case classes' parameters. This roughly translates to the following:

implicit val creatureReads = (
  (__ \ "name").read[String] and
  (__ \ "isDead").read[Boolean] and
  (__ \ "weight").read[Float]
)((name: String, isDead: Boolean, weight: Float) => new Creature(name, isDead, weight))

For missing fields you should indeed use readNullable and wrap your classes fields to Option . If there are sensible defaults for your optional fields, you can use orElse(Reads.pure(value)) instead.

Let's say weight is optional and isDead is false by default:

implicit val creatureReads = (
  (__ \ "name").read[String] and
  (__ \ "isDead").read[Boolean].orElse(Reads.pure(false)) and
  (__ \ "weight").readNullable[Float]
)(Creature.apply _)

Sometimes you don't even want to read something from JSON. In that case, one possibility is passing the value explicitly:

def creatureReads(createdAt: DateTime) = (
  (__ \ "name").read[String] and
  (__ \ "isDead").read[Boolean].orElse(Reads.pure(false)) and
  (__ \ "weight").readNullable[Float] and
  (__ \ "createdAt").read(createdAt)
)(Creature.apply _)

I find this to be much more readable:

implicit val createReads = new Reads[Creature] {
  override def reads(json: JsValue): JsResult[Creature] = {
    val creature = Creature(
      name = (json \ "name").as[String],
      isDead = (json \ "isDead").as[Boolean],
      weight = (json \ "weight").as[Float]
    )
    JsSuccess(creature)
  }
}

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