Write JSon deserialiser (Read[T]) for play 2.1.x - json

Play documentation has following example for creating a Read[T] for a case class. It returns T or throws an exception.
In 2.1.x, it is recommended that JsResult[T] is returned. It should either have T or all the errors. It is also recommended to use JsPath. I am unable to write the reads code for 2.1.x.
This is 2.0.x code from play documentation
case class Creature(
name: String,
isDead: Boolean,
weight: Float
)
In Play2.0.x, you would write your reader as following:
import play.api.libs.json._
implicit val creatureReads = new Reads[Creature] {
def reads(js: JsValue): Creature = {
Creature(
(js \ "name").as[String],
(js \ "isDead").as[Boolean],
(js \ "weight").as[Float]
)
}
}
For 2.1.x, I guess I'll have to do something like
implicit val creatureReads = new Reads[Creature] {
def reads(js: JsValue): JsResult[Creature] = {
(JsPath \ "key1").read[String]
/* at this point, I should either have key1's value or some error. I am clueless how to distinguish between the two and keep processing the rest of the JSon, accumulating all values or errors.*/
}
}

Explanation is in the documentation:
https://www.playframework.com/documentation/2.2.1/ScalaJsonCombinators
http://mandubian.com/2012/09/08/unveiling-play-2-dot-1-json-api-part1-jspath-reads-combinators/
So you reads in 2.1 could be written as
implicit val creatureReads: Reads[Creature] = (
(__ \ "name").read[String] and
(__ \ "isDead").read[Boolean] and
(__ \ "weight").read[Float]
)(Creature)

Related

JSON / BigInt Json deserializer in play / scala

I am trying to read some JSON String from request and convert them in case class in Play/Scala based REST application.
My code is something like ...
implicit val memberRead: Reads[MemberInfo] = (
(JsPath \ "memberId").readNullable[BigInt] and
(JsPath \ "firstName").read[String] and
(JsPath \ "lastName").read[String] and
(JsPath \ "janrainUUID").readNullable[String] and
(JsPath \ "phones").read[Seq[MemberPhone]] and
(JsPath \ "address").read[Seq[MemberAddress]]
)(MemberInfo.apply _)
implicit val addressRead: Reads[MemberAddress] = (
(JsPath \ "addressId").readNullable[BigInt] and
(JsPath \ "addressType").read[String] and
(JsPath \ "address").read[String] and
(JsPath \ "memberId").read[BigInt]
)(MemberAddress.apply _)
implicit val phoneRead: Reads[MemberPhone] = (
(JsPath \ "phoneId").readNullable[BigInt] and
(JsPath \ "phoneNumber").read[String] and
(JsPath \ "phoneType").read[String] and
(JsPath \ "primaryInd").read[String] and
(JsPath \ "memberId").read[BigInt]
)(MemberPhone.apply _)
But I am getting some compilation error(For all three readNullable[BigInt], memberid in memberRead, addressId in addressRead and phoneId in phoneRead ). Error is ...
No Json deserializer found for type BigInt. Try to implement an implicit Reads or Format for this type.
My Case class are some like this ...
case class MemberInfo(memberId : Option[BigInt],firstName : String, lastName : String,janrainUUID :Option[String] , phones : Seq[MemberPhone],address : Seq[MemberAddress])
case class MemberAddress(addressId:Option[BigInt],addressType:String,address:String,memberId:BigInt)
case class MemberPhone(phoneId : Option[BigInt], phoneNumber:String,phoneType:String,primaryInd:String,memberId:BigInt)
for janrainUUID :Option[String] I am not getting any compilation error , but for BigInt I am getting "No Json deserializer found for type BigInt"
Any one can explain why I am getting this error for BigInt and How can I resolve those? Actually those are PK value when I will do the DB operation for those, so they never may come with request. Is there any way to express that in play/scala like #ignore annotation in jersey.
Any help will be appreciated , thanks a lot...
You need to define serializers for BigInt in the following way:
implicit val BigIntWrite: Writes[BigInt] = new Writes[BigInt] {
override def writes(bigInt: BigInt): JsValue = JsString(bigInt.toString())
}
implicit val BigIntRead: Reads[BigInt] = Reads {
case JsString(value) => JsSuccess(scala.math.BigInt(value))
case JsNumber(value) => JsSuccess(value.toBigInt())
case unknown => JsError(s"Invalid BigInt")
}
Just add this before memberRead serializer and you are good to go and also add error handling for invalid BigInt.
play-json doesn't provide a Reads[BigInt]. It only provides a Reads[BigDecimal].
You can either write your own Reads[BigInt]:
implicit val bigIntReads: Reads[BigInt] = implicitly[Reads[BigDecimal]].map(_.toBigInt())
or use play's Reads[BigDecimal] and transform the result:
implicit val memberRead: Reads[MemberInfo] = (
(JsPath \ "memberId").readNullable[BigDecimal].map(_.toBigInt()) and
...
Edit: Both above solutions have the advantage of not re-inventing the wheel, they build on some well-tested infrastructure provided by play-json. As such they provide benefits that other solutions proposed for this question do not, mainly the correct handling of json string as well as numbers.
You can implement Format like this and use it in your companion object as implicit val:
import play.api.libs.json._
import play.api.libs.functional.syntax._
import scala.util.Try
object BigIntFormat extends Format[BigInt] {
override def reads(json: JsValue): JsResult[BigInt] = json match {
case JsNumber(n) => Try(JsSuccess(n.toBigInt)).getOrElse {
JsError(JsPath() -> JsonValidationError(s"error.expected.numeric(as BigInt), but got '$json'"))
}
case JsString(s) => Try(JsSuccess(BigInt(s))).getOrElse {
JsError(JsPath() -> JsonValidationError(s"error.expected.string(as BigInt), but got '$json'"))
}
case _ => JsError(JsPath() -> JsonValidationError("error.expected.string"))
}
override def writes(o: BigInt): JsValue = JsString(o.toString)
}
case class SomethingWithBigInt(id: BigInt, str: String)
object SomethingWithBigInt {
implicit val bigIntFormatter = BigIntFormat
implicit lazy val format: Format[SomethingWithBigInt] = ({
(JsPath \ "id").format[BigInt] and
(JsPath \ "str").format[String]
})(SomethingWithBigInt.apply, unlift(SomethingWithBigInt.unapply))
}

No Json formatter for Option[String]?

I am trying to marshall and un-marshall an Option[String] field to and from JSON. For my use-case, a None value should be marshaled as "null". Here is the code I have:
import org.scalatest.{FlatSpec, Matchers}
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
case class Person(
id: Int,
firstName: Option[String],
lastName: Option[String]
)
object Person {
implicit lazy val personFormat = (
(__ \ "id").format[Int] and
(__ \ "first_name").format[Option[String]] and
(__ \ "last_name").format[Option[String]]
)(Person.apply, unlift(Person.unapply))
}
class PersonSpec extends FlatSpec with Matchers {
"When Person instance is marshaled None fields " should
"be serialized as \"null\" values" in {
val person = Person(1, None, None)
import Person._
val json = Json.toJson(person)
println(json)
(json \ "id").as[Int] should be (1)
(json \ "first_name").get should be (JsNull)
(json \ "last_name").get should be (JsNull)
}
}
This results in the following compiler error:
PersonSpec.scala:19: No Json formatter found for type Option[String]. Try to implement an implicit Format for this type.
[error] (__ \ "first_name").format[Option[String]] and
[error] ^
These are some of the things I have tried:
Replacing (__ \ "first_name").format[Option[String]] with (__ \ "first_name").formatNullable[String] makes the compiler happy, but the test fails (""java.util.NoSuchElementException: None.get"") with the following output (from println(json))
{"id":1}
This confirms with formatNullable's behavior (don't render None valued fields).
Next, I replaced the format with a writes. Like so:
object Person {
implicit lazy val personWrite = (
(__ \ "id").write[Int] and
(__ \ "first_name").write[Option[String]] and
(__ \ "last_name").write[Option[String]]
)(unlift(Person.unapply))
}
Now, the compiler is happy and the test passes.
But I now need to implement a separate Reads. If I could, I would rather not as it violates DRY principle.
What am I doing wrong and when write[Option[...]] works perfectly why not format[Option[...]]?
Adding this code so that it is implicit-visible from your PersonFormat will make it work.
implicit def optionFormat[T: Format]: Format[Option[T]] = new Format[Option[T]]{
override def reads(json: JsValue): JsResult[Option[T]] = json.validateOpt[T]
override def writes(o: Option[T]): JsValue = o match {
case Some(t) ⇒ implicitly[Writes[T]].writes(t)
case None ⇒ JsNull
}
}
I think that in play it is assumed that option-valued fields should be treated optional at all, hence the behaviour you observed with formatNullable.
You can use:
(__ \ "first_name").formatNullable[String]

Play - Custom Json with Generic Crud

I have a Crud Controller in my application which works perfectly with JsonInception, but fail with a custom json converter.
Follow the given case class:
case class Validity(id: Option[UUID], objectType: String, since: DateTime, until: DateTime, objectId: UUID, validityTargetId: UUID, validityTargetType: String)
I have an object companion as follow:
object Validity extends CrudObject[Validity] {
implicit val reads = Json.reads[Validity]
implicit val writes = Json.writes[Validity]
}
Where my CrudObject is a trait with the given code:
trait CrudObject[T] {
val reads: Reads[T]
val writes: Writes[T]
}
This is needed since I'm working in a Generic Crud. Without this, Play is unable to find any implicit converter.
So my Generic Crud Controller, is something like this:
trait CrudController[T] extends Controller {
def service: ServiceModule[T]
def companion: CrudObject[T]
def search...
def insert...
implicit def reads: Reads[T] = companion.reads
implicit def writes: Writes[T] = companion.writes
and for each controller, I have the follow:
object ValidityController extends CrudController[Validity] {
override def service: GenericServiceModule[Validity] = ServiceModule
override def companion: CrudObject[Validity] = Validity
}
Ok, with this design in mind, as I said, works perfectly, I need to design a custom json converter. Yes, I know, it's pretty simple, but something is happening and I don't know how to get rid of that.
Now I'm trying to do the following:
implicit val reads: Reads[Validity] = (
(JsPath \ "id").read[String] and
(JsPath \ "objectType").read[String] and
(JsPath \ "since").read[Long] and
(JsPath \ "until").read[Long] and
(JsPath \ "objectId").read[String] and
(JsPath \ "validityTargetId").read[String] and
(JsPath \ "validityTargetType").read[String]
)(unlift(Validity.apply _))
and it gives me:
Type mismatch, expected: (NotInferedA) => Option[NotInferedB], actual: (CrudObject[Nothing]) => CrudObject[Nothing]
I believe this is happen cuz CrudObject is a trait and does not have apply and unapply.
Anyway, removing CrudObject gives me a similar error:
Type mismatch, expected: (NotInferedA) => Option[NotInferedB], actual: (Option[UUID], String, DateTime, DateTime, UUID, UUID, String) => Validity
but even if I can solve this, I can't imagine living without CrudObject due my GenericCrud.
Any Thoughts?
PS: Thanks to #m-z who has been giving me some assist through stackoverflow =)
UPDATE
I'm almost there with this approach:
implicit object validityFormat extends Format[Validity] {
override def writes(o: Validity): JsValue = {
Json.obj(
"id" -> JsString(o.id.getOrElse(null).toString),
"objectType" -> JsString(o.objectType),
"since" -> JsString(o.since.toString),
"until" -> JsString(o.since.toString),
"objectId" -> JsString(o.objectId.toString),
"validityTargetId" -> JsString(o.validityTargetId.toString),
"validityTargetType" -> JsString(o.validityTargetType))
}
override def reads(json: JsValue): JsResult[Validity] = {
JsSuccess(Validity(
(json \ "id").as[Option[UUID]],
(json \ "objectType").as[String],
(json \ "since").as[DateTime],
(json \ "until").as[DateTime],
(json \ "objectId").as[UUID],
(json \ "validityTargetId").as[UUID],
(json \ "validityTargetType").as[String])
)
}
}
Using this way, which is different from documentation Scala Combinators, I don't get the previous error, which is good, no type mismatch =)
Now I'm working out on to figure out how to convert to UUID and DateTime.
UPDATE
I did an example that works, but I accepted the #m-z answer because there are less boilerplate than mine, but both worked fine. The big difference is, in my approach I needed to provide some custom converters for DateTime and for UUID, whereas #m-z approach you don't!
implicit object UUIDFormatter extends Format[UUID] {
override def reads(json: JsValue): JsResult[UUID] = {
val uuid = json.validate[String]
JsSuccess(UUID.fromString(uuid.get))
}
override def writes(o: UUID): JsValue = {
JsString(o.toString)
}
}
implicit object DateTimeFormatter extends Format[DateTime] {
override def reads(json: JsValue): JsResult[DateTime] = {
val datetime = json.validate[Long]
JsSuccess(new DateTime(datetime.get))
}
override def writes(o: DateTime): JsValue = {
JsNumber(o.getMillis)
}
}
implicit object validityFormat extends Format[Validity] {
override def writes(o: Validity): JsValue = {
Json.obj(
"id" -> JsString(o.id.getOrElse(null).toString),
"objectType" -> JsString(o.objectType),
"since" -> JsNumber(o.since.getMillis),
"until" -> JsNumber(o.since.getMillis),
"objectId" -> JsString(o.objectId.toString),
"validityTargetId" -> JsString(o.validityTargetId.toString),
"validityTargetType" -> JsString(o.validityTargetType))
}
override def reads(json: JsValue): JsResult[Validity] = {
JsSuccess(Validity(
(json \ "id").as[Option[UUID]],
(json \ "objectType").as[String],
(json \ "since").as[DateTime],
(json \ "until").as[DateTime],
(json \ "objectId").as[UUID],
(json \ "validityTargetId").as[UUID],
(json \ "validityTargetType").as[String])
)
}
There are a couple problems here, but neither of them are relevant to generics, because you're dealing with the concrete type Validity.
First, the last argument using Reads combinators should be (Validity.apply _). You would only use unlift with Writes.
Second, the types in the combinators must map to the types in your Validity class.
implicit val reads: Reads[Validity] = (
(JsPath \ "id").readNullable[UUID] and // readNullable reads to Option
(JsPath \ "objectType").read[String] and
(JsPath \ "since").read[DateTime] and
(JsPath \ "until").read[DateTime] and
(JsPath \ "objectId").read[UUID] and
(JsPath \ "validityTargetId").read[UUID] and
(JsPath \ "validityTargetType").read[String]
)(Validity.apply _)
Reads already exist for UUID and DateTime, so this should work okay.
Similarly, Writes[Validity] would look like this:
implicit val writes: Writes[Validity] = (
(JsPath \ "id").writeNullable[UUID] and
(JsPath \ "objectType").write[String] and
(JsPath \ "since").write[DateTime] and
(JsPath \ "until").write[DateTime] and
(JsPath \ "objectId").write[UUID] and
(JsPath \ "validityTargetId").write[UUID] and
(JsPath \ "validityTargetType").write[String]
)(unlift(Validity.unapply))
Hi I Think that your case class has an option parameter your Reads must have readnullable in the optional parameters must be like this , I'm not sure with UIID and String
case class Validity(id: Option[UUID], objectType: String, since: DateTime, until: DateTime, objectId: UUID, validityTargetId: UUID, validityTargetType: String)
implicit val reads: Reads[Validity] = (
(JsPath \ "id").readNullable[UIID] and
(JsPath \ "objectType").read[String] and
(JsPath \ "since").read[Long] and
(JsPath \ "until").read[Long] and
(JsPath \ "objectId").read[String] and
(JsPath \ "validityTargetId").read[String] and
(JsPath \ "validityTargetType").read[String]
)(unlift(Validity.apply _))

Custom Json Writes with combinators - not all the fields of the case class are needed

I'm trying to write a custom Json serializer in play for a case class but I don't want it to serialize all the fields of the class. I'm pretty new to Scala, so that is surely the problem but this is what I tried so far:
case class Foo(a: String, b: Int, c: Double)
Now the default way of doing this, as far as I saw in the examples is:
implicit val fooWrites: Writes[Foo] = (
(__ \ "a").write[String] and
(__ \ "b").write[Int]
(__ \ "c").write[Double]
) (unlift(Foo.unapply))
But what if I want to omit "c" from the Json output? I've tried this so far but it doesn't compile:
implicit val fooWritesAlt: Writes[Foo] = (
(__ \ "a").write[String] and
(__ \ "b").write[Int]
) (unlift({(f: Foo) => Some((f.a, f.b))}))
Any help is greatly appreciated!
If you are using Playframework 2.2 (not sure about earlier versions, but it should work as well) try this:
implicit val writer = new Writes[Foo] {
def writes(foo: Foo): JsValue = {
Json.obj("a" -> foo.a,
"b" -> foo.b)
}
}
What I usually do is convert a field to None and use writeNullable on it:
implicit val fooWrites: Writes[Foo] = (
(__ \ "a").write[String] and
(__ \ "b").write[Int]
(__ \ "c").writeNullable[Double].contramap((_: Double) => None)
) (unlift(Foo.unapply))
Instead of specifying a combinator which produces an OWrites instance, one can directly construct an OWrites just as well:
val ignore = OWrites[Any](_ => Json.obj())
implicit val fooWrites: Writes[Foo] = (
(__ \ "a").write[String] and
(__ \ "b").write[Int] and
ignore
) (unlift(Foo.unapply))
This will ignore any value of the case class at that position and simply always return an empty JSON object.
This is very easy to do with the Play's JSON transformers:
val outputFoo = (__ \ 'c).json.prune
and then apply transform(outputFoo) to your existing JsValue:
val foo: Foo
val unprunedJson: JsValue = Json.toJson(foo)
unprunedJson.transform(outputFoo).map { jsonp =>
Ok(Json.obj("foo" -> jsonp))
}.recoverTotal { e =>
BadRequest(JsError.toFlatJson(e))
}
see here http://mandubian.com/2013/01/13/JSON-Coast-to-Coast/
What version of Play are you using? fooWritesAlt compiles for me using 2.1.3. One thing to note is that I needed to explicitly use the writes object to write the partial JSON object, i.e.
fooWritesAt.writes(Foo("a", 1, 2.0))
returns
{"a": "a", "b": 2}

Play2.1 JSON Format with Empty String Validation

I am attempting to validate JSON when unmarshalling to an object in Play2.1. The Format object I have defined only validates when a field is absent in the JSON, but I want to validate that fields are nonEmpty strings. Is this possible? I've tried specifying the minLength() constraint (as seen here) in the reads() call, but I get a compiler error saying minLength can't be found. Is that only for the tuple approach?
See the following Specs2 Junit test which fails now, but should pass when the constraint is defined properly:
import org.specs2.mutable._
import play.api.libs.json._
class SimpleValidation extends SpecificationWithJUnit{
private val badPayload: JsValue = Json.obj(
"simpleValue1" -> "mySimpleValue", // Comment this line out to pass test
"simpleValue2" -> ""
)
"An IssueFormat" should {
"validate when unmarshalling" in {
badPayload.validate[SimpleObj].fold(
valid = (res => {
// Fail if valid
failure("Payload should have been invalid")
}),
invalid = (e => {
// Should be one error
e.length mustBeEqualTo(1)
}))
}
}
}
import play.api.libs.functional.syntax._
case class SimpleObj(simpleValue1: String, simpleValue2: String)
object SimpleObj {
val simpleReads = (
(__ \ "simpleValue1").read[String] and
(__ \ "simpleValue2").read[String])(SimpleObj.apply _) // read[String](minLength(0)) yields compiler error
val simpleWrites = (
(__ \ "simpleValue1").write[String] and
(__ \ "simpleValue2").write[String])(unlift(SimpleObj.unapply))
implicit val simpleFormat: Format[SimpleObj] = Format(simpleReads, simpleWrites)
}
You can use the minLength in Reads:
import play.api.libs.json.Reads._
Then minLength should be available, however, try this format instead:
implicit val simpleReads = (
(__ \ "simpleValue1").read(minLength[String](1)) and
(__ \ "simpleValue2").read(minLength[String](1))(SimpleObj.apply _)
After looking through the Play2.1 documentation some more, I was able to add a custome read validator. If you replace the SimpleObj from the original question, with the following, the test case will pass. Not sure if there is a simpler way to do this, but this definitely works:
object SimpleObj {
// defines a custom reads to be reused
// a reads that verifies your value is not equal to a give value
def notEqual[T](v: T)(implicit r: Reads[T]): Reads[T] = Reads.filterNot(ValidationError("validate.error.unexpected.value", v))(_ == v)
implicit val simpleReads = (
(__ \ "simpleValue1").read[String](notEqual("")) and
(__ \ "simpleValue2").read[String](notEqual("")))(SimpleObj.apply _)
val simpleWrites = (
(__ \ "simpleValue1").write[String] and
(__ \ "simpleValue2").write[String])(unlift(SimpleObj.unapply))
implicit val simpleFormat: Format[SimpleObj] = Format(simpleReads, simpleWrites)
}