Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Play Framework 2.4 Writes[-A] vs OWrites[-A], Format[A] vs OFormat[A]. Purpose?

What's the difference between Writes[-A] and OWrites[-A] in PlayFramework Json library? I have used Writes[A] but I can't figure out what's the purpose of OWrites. The same question applies to Format[A] vs OFormat[A].

The source code can be found here for Writes and here for Formats. I have looked at them but I can't figure out the difference on their usages.

like image 389
vicaba Avatar asked Aug 29 '15 19:08

vicaba


1 Answers

Often you know that an encoder will always produce a JSON object (as opposed to an arbitrary JSON value). Tracking this fact in the type system makes it possible to work with the output of such an encoder without jumping through the hoops that would be normally be necessary.

For example, suppose we've got a simple class:

class Foo(val name: String, val age: Long) 

And we write a Writes instance like this:

import play.api.libs.functional.syntax._ import play.api.libs.json._  implicit val fooWrites: Writes[Foo] = (   (__ \ 'name).write[String] and (__ \ 'age).write[Long] )(foo => (foo.name, foo.age)) 

Now we can write the following:

scala> val json = fooWrites.writes(new Foo("McBar", 101)) json: play.api.libs.json.JsValue = {"name":"McBar","age":101} 

Now suppose that for whatever reason we want to get a list of the field names. We have to write something like this:

scala> json.as[JsObject].keys res0: scala.collection.Set[String] = Set(name, age) 

Instead of this:

scala> json.keys <console>:17: error: value keys is not a member of play.api.libs.json.JsValue               json.keys                    ^ 

But of course we know that json will always be a JsObject. The problem is that the compiler doesn't. OWrites fixes this.

implicit val fooWrites: OWrites[Foo] = (    (__ \ 'name).write[String] and (__ \ 'age).write[Long] )(foo => (foo.name, foo.age)) 

And then:

scala> val json = fooWrites.writes(new Foo("McBar", 101)) json: play.api.libs.json.JsObject = {"name":"McBar","age":101}  scala> json.keys res1: scala.collection.Set[String] = Set(name, age) 

The output of writes on OWrites is statically typed as a JsObject, so we can use .keys without the unsafe as[JsObject] cast.

(As a side note, I'm not personally a fan of making method return types more specific in subclasses, and I've taken a slightly different approach to solving this problem in circe.)

like image 58
Travis Brown Avatar answered Sep 24 '22 13:09

Travis Brown