6
votes

I have a simple question regarding rendering JSON object from a Scala class. Why do I have to implemet deserializer ( read, write ).

I have the following case class:

case class User(firstname:String, lastname:String, age:Int)

And in my controller:

val milo:User = new User("Sam","Fisher",23);

Json.toJson(milo);

I get compilation error: No Json deserializer found for type models.User. Try to implement an implicit Writes or Format for this type.

In my previous project I had to implement a reader,writer object in the class for it to work and I find it very annoying.

object UserWebsite {
  implicit object UserWebsiteReads extends Format[UserWebsite] {

    def reads(json: JsValue) = UserWebsite(
      (json \ "email").as[String],
      (json \ "url").as[String],
      (json \ "imageurl").as[String])

    def writes(ts: UserWebsite) = JsObject(Seq(
      "email" -> JsString(ts.email),
      "url" -> JsString(ts.url),
      "imageurl" -> JsString(ts.imageurl)))
  }
} 
5

5 Answers

9
votes

I really recommend to upgrade to play 2.1-RC1 because here, JSON writers/readers are very simple to be defined (more details here)

But in order to help you to avoid some errors, I will give you a hint with imports: - use these imports only! (notice that json.Reads is not included)

import play.api.libs.json._
import play.api.libs.functional.syntax._
import play.api.libs.json.Writes._

and you only have to write this code for write/read your class to/from Json (of course you will have User instead of Address:

implicit val addressWrites = Json.writes[Address]
implicit val addressReads = Json.reads[Address]

Now, they will be used automatically:

Example of write:

Ok(Json.toJson(entities.map(s => Json.toJson(s))))

Example of read(I put my example of doing POST for creating an entity by reading json from body) please notice addressReads used here

def create = Action(parse.json) { request =>
        request.body.validate(addressReads).map { entity =>
          Addresses.insert(entity)
          Ok(RestResponses.toJson(RestResponse(OK, "Succesfully created a new entity.")))
        }.recover { Result =>
          BadRequest(RestResponses.toJson(RestResponse(BAD_REQUEST, "Unable to transform JSON body to entity.")))
        }
}

In conclusion, they tried (and succeded) to make things very simple regarding JSON.

3
votes

If you are using play 2.0.x you can do

import com.codahale.jerkson.Json._

generate(milo)

generate uses reflection to do it.

In play 2.1 you can use Json.writes to create a macro for that implicit object you had to create. No runtime reflection needed!

import play.api.libs.json._
import play.api.libs.functional.syntax._

implicit val userWrites = Json.writes[User]
Json.toJson(milo)
0
votes

I have been using jerkson (which basically is wrapper to jackson) in my project to convert objects to json string.

The simplest way to do that is:

import com.codehale.jerkson.Json._
...
generate(milo)
...

If you need to configure the ObjectMapper (e.g. adding custom serializer/deserializer, configuring output format, etc.), you can do it by creating object which extends com.codehale.jerkson.Json class.

package utils

import org.codehaus.jackson.map._
import org.codehaus.jackson.{Version, JsonGenerator, JsonParser}
import com.codahale.jerkson.Json
import org.codehaus.jackson.map.module.SimpleModule
import org.codehaus.jackson.map.annotate.JsonSerialize

object CustomJson extends Json {

  val module = new SimpleModule("CustomSerializer", Version.unknownVersion())

  // --- (SERIALIZERS) ---
  // Example:
  // module.addSerializer(classOf[Enumeration#Value], EnumerationSerializer)
  // --- (DESERIALIZERS) ---
  // Example:
  // module.addDeserializer(classOf[MyEnumType], new EnumerationDeserializer[MyEnumType](MyEnumTypes))

  mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL)
  mapper.setSerializationConfig(mapper.getSerializationConfig.without(SerializationConfig.Feature.WRITE_NULL_MAP_VALUES))
  mapper.registerModule(module)
}

To use it in your codes:

import utils.CustomJson._
...
generate(milo)
...
0
votes

In fact, this is very simple. Firstly import:

import play.api.libs.json._

Thanks to the fact, that User is a case class you can automatically create Json writes using Json.writes[]:

val milo:User = new User("Millad","Dagdoni",23)
implicit val userImplicitWrites = Json.writes[User]
Json.toJson(milo)

I haven't found it in the docs, but here is the link to the api: http://www.playframework.com/documentation/2.2.x/api/scala/index.html#play.api.libs.json.Json$

0
votes

In your case, I'd use the JSON.format macro.

import play.api.libs.json._
implicit val userFormat = Json.format[User]
val milo = new User("Sam", "Fisher", 23)
val json = Json.toJson(milo)