parse json string to a scala object - json

I use play framework version 2.2.1 with scala.
I have a large json-string that i wish to construct an object from.
I have written the formatters for the classes.
My question is what are the steps i am supposed to cast that string to concrete class.
e.g of code that i've tried:
val something = scala.util.parsing.json.JSON.parseFull(jsonContent).asInstanceOf[HsmTenant] //This has stucked my debug session and operation never completes
Or
val concreteClass = Json.parse(jsonContent).asInstanceOf[T] //message: "Error processing request - Failed to migrate, Exception=play.api.libs.json.JsObject cannot be cast to migration.hsm.HsmTenant"
When trying:
Json.parse(jsonContent).as[T]
I get the following:
Multiple markers at this line
- No Json deserializer found for type migration.hsm.HsmTenant. Try to implement an implicit Reads or Format for this type.
- not enough arguments for method as: (implicit fjs: play.api.libs.json.Reads[migration.hsm.HsmTenant])migration.hsm.HsmTenant. Unspecified value
parameter fjs.
- No Json deserializer found for type migration.hsm.HsmTenant. Try to implement an implicit Reads or Format for this type.
- not enough arguments for method as: (implicit fjs: play.api.libs.json.Reads[migration.hsm.HsmTenant])migration.hsm.HsmTenant. Unspecified value
parameter fjs.
- Line breakpoint:HsmParser [line: 16] - parse(jsonContent: String): migration.hsm.HsmTenant
My read and writes looks like: and they compile:
trait HsmFormats {
implicit val hsmRetailerFormat = Json.format[Retailer]
implicit val hsmproductFormat = new Format[HsmProduct]
{
def writes(item: HsmProduct): JsValue =
{
val retailers = item.r.getOrElse(List[Retailer]())
val images = item.images.getOrElse(List[String]())
Json.obj(
"u" -> item.u,
"s" -> item.s,
"z" -> item.z,
"n" -> item.n,
"v" -> item.v,
"vu" -> item.vu,
"t" -> item.t,
"r" -> retailers,
"images" -> images
)
}
def reads(json: JsValue): JsResult[HsmProduct] =
{
val retailers = (json \ "r").as[Option[List[Retailer]]]
var retaliersReal:Option[List[Retailer]] = None
if (retailers.isDefined)
{
retaliersReal = Some(retailers.get)
}
val images = (json \ "images").as[Option[List[String]]]
var imagesReal:Option[List[String]] = None
if (images.isDefined)
{
imagesReal = Some(images.get)
}
JsSuccess(new HsmProduct(
(json \ "u").as[String],
(json \ "s").as[Int],
(json \ "z").as[Int],
(json \ "n").as[String],
(json \ "v").as[String],
(json \ "vu").as[String],
(json \ "t").as[String],
retailers,
imagesReal
))
}
}
implicit val hsmTenantFormat = new Format[HsmTenant]
{
def writes(tenant: HsmTenant): JsValue =
{
val items = tenant.items.getOrElse(List[HsmProduct]())
Json.obj(
"items" -> tenant.items,
"prefixAndroid" -> tenant.prefixAndroid,
"prefixIOS" -> tenant.prefixIOS,
"er" -> tenant.er,
"erMessage" -> tenant.erMessage
)
}
def reads(json: JsValue): JsResult[HsmTenant] =
{
val items = (json \ "items").as[Option[List[HsmProduct]]]
var itemsReal:Option[List[HsmProduct]] = None
if (items.isDefined)
{
itemsReal = Some(items.get)
}
JsSuccess(new HsmTenant(
itemsReal,
(json \ "prefixAndroid").as[String],
(json \ "prefixIOS").as[String],
(json \ "er").as[Int],
(json \ "erMessage").as[String]
))
}
}

This should work if your Formats are correct:
import play.api.libs.json.Json
Json.parse(jsonContent).as[T]

What was the problem ?
It was not issue if import the Formats class, my format is a trait, so i must extend that trait in classes i use it.

Related

Play JSON serialize/deserialize

I'm trying to go create some formats for this:
case class Work[T](todo: Seq[T], failed: Seq[T], success: Seq[T])
object Work {
implicit def format[T](implicit r: Reads[T], w: Writes[T]): Format[Work[T]] = Json.format[Work[T]]
}
object InternalMessage {
implicit def format[D, R](implicit
rD: Reads[D],
wD: Writes[D],
rR: Reads[R],
wR: Writes[R]
): Format[InternalMessage[D, R]] = Json.format[InternalMessage[D, R]]
}
case class InternalMessage[D, R](
download: List[Work[D]],
refine: List[Work[R]],
numberOfTries: Int
)
This doesn't work and I don't understand why. The error is
[error] /home/oleber/develop/data-platform/modules/importerTemplate/src/main/scala/template/TemplateModel.scala:46: No apply function found matching unapply parameters
[error] implicit def format[T](implicit r: Reads[T], w: Writes[T]): Format[Work[T]] = Json.format[Work[T]]
[error] ^
[error] /home/oleber/develop/data-platform/modules/importerTemplate/src/main/scala/template/TemplateModel.scala:55: No apply function found matching unapply parameters
[error] ): Format[InternalMessage[D, R]] = Json.format[InternalMessage[D, R]]
Thanks for any help
AFAIU you can't generate JSON Format for such data types using Play 2.5 macros. What is limiting you is that you use Seq[T] i.e. you want JSON serialization for a generic class that has fields that use some complex type built from that generic type rather than just raw generic T. Relevant code seems to be in the JsMacroImpl lines 135-141. In those lines inside paramsMatch inner method of maybeApply calculation the macro checks if there are an apply and unapply methods with matching (complement) signatures:
val maybeApply = applies.collectFirst {
case (apply: MethodSymbol) if hasVarArgs && {
// Option[List[c.universe.Type]]
val someApplyTypes = apply.paramLists.headOption.map(_.map(_.asTerm.typeSignature))
val someInitApply = someApplyTypes.map(_.init)
val someApplyLast = someApplyTypes.map(_.last)
val someInitUnapply = unapplyReturnTypes.map(_.init)
val someUnapplyLast = unapplyReturnTypes.map(_.last)
val initsMatch = someInitApply == someInitUnapply
val lastMatch = (for {
lastApply <- someApplyLast
lastUnapply <- someUnapplyLast
} yield lastApply <:< lastUnapply).getOrElse(false)
initsMatch && lastMatch
} => apply
case (apply: MethodSymbol) if {
val applyParams = apply.paramLists.headOption.
toList.flatten.map(_.typeSignature)
val unapplyParams = unapplyReturnTypes.toList.flatten
def paramsMatch = (applyParams, unapplyParams).zipped.forall {
case (TypeRef(NoPrefix, applyParam, _),
TypeRef(NoPrefix, unapplyParam, _)) => // for generic parameter
applyParam.fullName == unapplyParam.fullName
case (applyParam, unapplyParam) => applyParam =:= unapplyParam
}
(applyParams.size == unapplyParams.size && paramsMatch)
} => apply
}
As you can see in the relevant (non-hasVarArgs branch), this code only handles two cases: when the types in apply and unapply match exactly or when the same raw generic type is used. Your case of Seq[T] (as well as other complex generic types) is not handled here and the error you get is generated just a few lines down when maybeApply is empty:
val (tparams, params) = maybeApply match {
case Some(apply) => {
apply.typeParams -> apply.paramLists.head
// assume there is a single parameter group
}
case None => c.abort(c.enclosingPosition, "No apply function found matching unapply parameters")
}
In Play-JSON 2.6 this code was significantly re-worked and now it seems to support your case as well (see conforms inner method).
If upgrading to (not released yet) Play-JSON 2.6 is not acceptable, you can create Formats object(s) yourself using JSON Reads/Writes/Format Combinators doc. Something like this should work for you:
implicit def format[T](implicit r: Reads[T], w: Writes[T]): Format[Work[T]] = {
val workReads = (
(JsPath \ "todo").read[Seq[T]] and
(JsPath \ "failed").read[Seq[T]] and
(JsPath \ "success").read[Seq[T]]
) (Work.apply[T] _)
val workWrites = (
(JsPath \ "todo").write[Seq[T]] and
(JsPath \ "failed").write[Seq[T]] and
(JsPath \ "success").write[Seq[T]]
) (unlift(Work.unapply[T]))
new Format[Work[T]] {
override def reads(json: JsValue): JsResult[Work[T]] = workReads.reads(json)
override def writes(o: Work[T]): JsValue = workWrites.writes(o)
}
}

PlayFramework in Scala, Reads - error in case of unknown key? [duplicate]

Play's JSON serialization is by default permissive when serializing from JSON into a case class. For example.
case class Stuff(name: String, value: Option[Boolean])
implicit val stuffReads: Reads[Stuff] = (
( __ \ 'name).read[String] and
( __ \ 'value).readNullable[Boolean]
)(Stuff.apply _)
If the following JSON was received:
{name: "My Stuff", value: true, extraField: "this shouldn't be here"}
It will succeed with a 'JsSuccess' and discard the 'extraField'.
Is there a way to construct the Json Reads function to have it return a JsError if there are 'unhandled' fields?
You can verify that the object doesn't contain extra keys before performing your own decoding:
import play.api.data.validation.ValidationError
def onlyFields(allowed: String*): Reads[JsObject] = Reads.filter(
ValidationError("One or more extra fields!")
)(_.keys.forall(allowed.contains))
Or if you don't care about error messages (and that one's not very helpful, anyway):
def onlyFields(allowed: String*): Reads[JsObject] =
Reads.verifying(_.keys.forall(allowed.contains))
And then:
implicit val stuffReads: Reads[Stuff] = onlyFields("name", "value") andThen (
(__ \ 'name).read[String] and
(__ \ 'value).readNullable[Boolean]
)(Stuff)
The repetition isn't very nice, but it works.
Inspired from Travis' comment to use LabelledGeneric I was able achieve compile time safe solution.
object toStringName extends Poly1 {
implicit def keyToStrName[A] = at[Symbol with A](_.name)
}
case class Foo(bar: String, boo: Boolean)
val labl = LabelledGeneric[Foo]
val keys = Keys[labl.Repr].apply
now keys.map (toStringName).toList will give you
res0: List[String] = List(bar, boo)

How to enforce strict serialization of JSON in Play 2.x

Play's JSON serialization is by default permissive when serializing from JSON into a case class. For example.
case class Stuff(name: String, value: Option[Boolean])
implicit val stuffReads: Reads[Stuff] = (
( __ \ 'name).read[String] and
( __ \ 'value).readNullable[Boolean]
)(Stuff.apply _)
If the following JSON was received:
{name: "My Stuff", value: true, extraField: "this shouldn't be here"}
It will succeed with a 'JsSuccess' and discard the 'extraField'.
Is there a way to construct the Json Reads function to have it return a JsError if there are 'unhandled' fields?
You can verify that the object doesn't contain extra keys before performing your own decoding:
import play.api.data.validation.ValidationError
def onlyFields(allowed: String*): Reads[JsObject] = Reads.filter(
ValidationError("One or more extra fields!")
)(_.keys.forall(allowed.contains))
Or if you don't care about error messages (and that one's not very helpful, anyway):
def onlyFields(allowed: String*): Reads[JsObject] =
Reads.verifying(_.keys.forall(allowed.contains))
And then:
implicit val stuffReads: Reads[Stuff] = onlyFields("name", "value") andThen (
(__ \ 'name).read[String] and
(__ \ 'value).readNullable[Boolean]
)(Stuff)
The repetition isn't very nice, but it works.
Inspired from Travis' comment to use LabelledGeneric I was able achieve compile time safe solution.
object toStringName extends Poly1 {
implicit def keyToStrName[A] = at[Symbol with A](_.name)
}
case class Foo(bar: String, boo: Boolean)
val labl = LabelledGeneric[Foo]
val keys = Keys[labl.Repr].apply
now keys.map (toStringName).toList will give you
res0: List[String] = List(bar, boo)

Type mismatch transforming Sequence to JSON in play 2.2.2

Hi there i found several questions like mine here on stackoverflow but none of the provided solutions seem to work for me.
I`m testing the playframework (2.2.2) and trying to figure out how to generate a json Format for a Sequence of objects.
case class Captain(
val name: String,
val secondname: String,
val surname: String
)
So in my Controller i have the following Action:
def captains= Action {
Ok(getCaptains).as("application/json")
}
and the following method:
def getCaptains= {
val seq = Seq[Captain](new Captain("james", "tiberius", "kirk"), new Captain("jean", "luc", "picard"))
val jsonOutput = Json.toJson(seq).as(Seq[Captain] _)
}
Of course this triggers: "No Json serializer found for type Seq[models.Captain]. Try to implement an implicit Writes or Format for this type."
So following the play documentation and several threads here and around the web i used an
object Cpatain {
}
And within the object i tried the following solutions:
Writer approaches:
1:
implicit def writes(cap: Captain): JsValue = {
val capSeq = Seq(
"name" -> JsString(cap.name),
"secondname" -> JsString(cap.secondname),
"surname" -> JsString(cap.surname)
)
JsObject(capSeq)
}
2:
implicit val CaptainWrites = new Writes[Captain] {
def writes(cap: Captain): JsValue = {
Json.obj(
"name" -> cap.name,
"secondname" -> cap.secondname,
"surname" -> cap.surname
)
}
}
Reader approaches:
1:
implicit val regReads: Reads[Captain] = (__ \ "captains").read(
(__ \ "name").read[String] and
(__ \ "secondname").read[String] and
(__ \ "surname").read[String]
tupled
) map Captain.apply _
2:
implicit val CaptainReads =
(JsPath \ "captains").read(
(JsPath \ "name").read[String] and
(JsPath \ "secondname").read[String] and
(JsPath \ "surname").read[String]
tupled
)
And also:
implicit object captainFormat extends Format[Captain] {
def writes(cap: Captain): JsValue = {
Json.obj(
"name" -> JsString(cap.name),
"secondname" -> JsString(cap.secondname),
"surname" -> JsString(cap.surname)
)
}
def reads(json: JsValue): Captain= Captain(
(json \ "name").as[String],
(json \ "secondname").as[String],
(json \ "surname").as[String]
)
}
But in either way i result in:
"type mismatch; found : Seq[models.Captain] => Seq[models.Captain] required: play.api.libs.json.Reads[?]"
This is also mentioned in:
type mismatch error when creating Reads for Play 2.1
and
Json Writes in Play 2.1.1
But as you can see i allready tried the provided solutions without success.
So it seems i`m missing somesthing. I would appreciate any help!!
So i managed to get it running by using:
implicit val capReads: Reads[Captain] = (
(__ \ "name").read[String] ~
(__ \ "secondname").read[String] ~
(__ \ "surname").read[String]
)(Captain.apply _)
implicit def writes(cap: Captain): JsValue = {
Json.obj(
"name" -> JsString(cap.name),
"secondname" -> JsString(cap.surname),
"surname" -> JsString(cap.surname2)
)
}

Outputting 'null' for Option[T] in play-json serialization when value is None

I'm using play-json's macros to define implicit Writes for serializing JSON. However, it seems like by default play-json omits fields for which Option fields are set to None. Is there a way to change the default so that it outputs null instead? I know this is possible if I define my own Writes definition, but I'm interested in doing it via macros to reduce boilerplate code.
Example
case class Person(name: String, address: Option[String])
implicit val personWrites = Json.writes[Person]
Json.toJson(Person("John Smith", None))
// Outputs: {"name":"John Smith"}
// Instead want to output: {"name":"John Smith", "address": null}
The Json.writes macro generates a writeNullable[T] for optional fields. Like you know (or not), writeNullable[T] omits the field if the value is None, whereas write[Option[T]] generates a null field.
Defining a custom writer is the only option you have to get this behavior.
(
(__ \ 'name).write[String] and
(__ \ 'address).write[Option[String]]
)(unlift(Person.unapply _))
You can use a custom implicit JsonConfiguration, see Customize the macro to output null
implicit val config = JsonConfiguration(optionHandlers = OptionHandlers.WritesNull)
implicit val personWrites = Json.writes[Person]
Json.toJson(Person("John Smith", None))
Not a real solution for you situation. But slightly better than having to manually write the writes
I created a helper class that can "ensure" fields.
implicit class WritesOps[A](val self: Writes[A]) extends AnyVal {
def ensureField(fieldName: String, path: JsPath = __, value: JsValue = JsNull): Writes[A] = {
val update = path.json.update(
__.read[JsObject].map( o => if(o.keys.contains(fieldName)) o else o ++ Json.obj(fieldName -> value))
)
self.transform(js => js.validate(update) match {
case JsSuccess(v,_) => v
case err: JsError => throw new JsResultException(err.errors)
})
}
def ensureFields(fieldNames: String*)(value: JsValue = JsNull, path: JsPath = __): Writes[A] =
fieldNames.foldLeft(self)((w, fn) => w.ensureField(fn, path, value))
}
so that you can write
Json.writes[Person].ensureFields("address")()
Similar answer to above, but another syntax for this:
implicit val personWrites = new Writes[Person] {
override def writes(p: Person) = Json.obj(
"name" -> p.name,
"address" -> p.address,
)
}
This is simple:
implicit val personWrites = new Writes[Person] {
override def writes(p: Person) = Json.obj(
"name" -> p.name,
"address" -> noneToString(p.address),
)
}
def optToString[T](opt: Option[T]) =
if (opt.isDefined) opt.get.toString else "null"
You can define something like this :
implicit class JsPathExtended(path: JsPath) {
def writeJsonOption[T](implicit w: Writes[T]): OWrites[Option[T]] = OWrites[Option[T]] { option =>
option.map(value =>
JsPath.createObj(path -> w.writes(value))
).getOrElse(JsPath.createObj(path -> JsNull))
}
}
And if you are using play framework :
implicit val totoWrites: Writes[Toto] = (
(JsPath \ "titre").write[String] and
(JsPath \ "option").writeJsonOption[String] and
(JsPath \ "descriptionPoste").writeNullable[String]
) (unlift(Toto.unapply))
implicit val totoReads: Reads[Toto] = (
(JsPath \ "titre").read[String] and
(JsPath \ "option").readNullable[String] and
(JsPath \ "descriptionPoste").readNullable[String]
) (Toto.apply _)
You may wrap your option and redefine serialization behavior:
case class MyOption[T](o: Option[T])
implicit def myOptWrites[T: Writes] = Writes[MyOption[T]](_.o.map(Json.toJson).getOrElse(JsNull))
CAVEATS:
1)this approach is good only for case classes used solely as a serialization protocol definition. If you are reusing some data model classes in controllers - define custom serializers for them not to pollute the model.
2)(related only to Option) if you use the same class for writes and reads. Play will require the wrapped fields to be present (possibly null) during deserialization.
P.S.: Failed to do the same with type tagging. Compiler error is like
No instance of play.api.libs.json.Writes is available for tag.<refinement> (given the required writes were explicitly defined).
Looks like Play's macro fault.