26

there are two classes Foo and Bar. Foo contains a field of Bar. The question is, how do I implement an implicit json Writes for class Foo?

Here is the code:

package models

import play.api.libs.json._

case class Foo(id: String, bar: Bar)

object Foo {
  implicit val implicitFooWrites = new Writes[Foo] {
    def writes(foo: Foo): JsValue = {
      Json.obj(
        "id" -> foo.id,
        "bar" -> foo.bar
      )
    }
  }
}

case class Bar(x: String, y: Int)

object Bar {
  implicit val implicitBarWrites = new Writes[Bar] {
    def writes(bar: Bar): JsValue = {
      Json.obj(
        "x" -> bar.x,
        "y" -> bar.y
      )
    }
  }
}

When I try to compile, I get the following error:

No Json deserializer found for type models.Bar. Try to implement an implicit Writes or Format for this type.

I don't understand this compiler error, since I implemented an implicit Writes for models.Bar class. What is the problem here?

1 Answer 1

35

It's a question of visibility, when declaring the implicit Writes[Foo] you are not making visible the implicit Writes[Bar] to it:

scala> :paste
// Entering paste mode (ctrl-D to finish)

import play.api.libs.json._

case class Bar(x: String, y: Int)

object Bar {
  implicit val implicitBarWrites = new Writes[Bar] {
    def writes(bar: Bar): JsValue = {
      Json.obj(
        "x" -> bar.x,
        "y" -> bar.y
      )
    }
  }
}

case class Foo(id: String, bar: Bar)

object Foo {

  import Bar._

  implicit val implicitFooWrites = new Writes[Foo] {
    def writes(foo: Foo): JsValue = {
      Json.obj(
        "id" -> foo.id,
        "bar" -> foo.bar
      ) 
    } 
  }     
}

// Exiting paste mode, now interpreting.

import play.api.libs.json._
defined class Bar
defined module Bar
defined class Foo
defined module Foo

scala> Json.prettyPrint(Json.toJson(Foo("23", Bar("x", 1))))
res0: String = 
{
  "id" : "23",
  "bar" : {
    "x" : "x",
    "y" : 1
  }
}

Also, if you're using Play 2.1+ make sure to check out the brand new use of 2.10's macros: http://www.playframework.com/documentation/2.1.0/ScalaJsonInception

If you're happy with the use of the case classes and the val/vars' names being used as keys in the json output, as in your case BTW, then you can use the two one-liners:

implicit val barFormat = Json.writes[Bar]
implicit val fooFormat = Json.writes[Foo]

That will give you the exact equivalent:

scala> import play.api.libs.json._
import play.api.libs.json._

scala> case class Bar(x: String, y: Int)
defined class Bar

scala> case class Foo(id: String, bar: Bar)
defined class Foo

scala> implicit val barWrites = Json.writes[Bar]
barWrites: play.api.libs.json.OWrites[Bar] = play.api.libs.json.OWrites$$anon$2@257cae95

scala> implicit val fooWrites = Json.writes[Foo]
fooWrites: play.api.libs.json.OWrites[Foo] = play.api.libs.json.OWrites$$anon$2@48f97e2a

scala> Json.prettyPrint(Json.toJson(Foo("23", Bar("x", 1))))
res0: String = 
{
  "id" : "23",
  "bar" : {
    "x" : "x",
    "y" : 1
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Nice. Thanks a lot. Just declaring the 'Bar' class before the 'Foo' class solves the problem. And thanks for the hint with the new Play2.1 json features. This really shortens the number of lines to write.
I am trying to figure out how to use reactive mongodb by following their example and it wouldn't compile. After reading this post, I was able to modify the existing and get it to post a user. I am using 2.4 now. Are there any other shortcuts or changes that now exist in 2.4?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.