Parse Json Sequence without keys in playframework - json

I am trying to parse a Json object that consists only of an top level array without a key.
import play.api.libs.json._
import play.api.libs.functional.syntax._
case class Name(first: String, last: String)
case class Names(names: Seq[Name])
implicit val NameF = Json.format[Name]
val s = """[{"first": "A", "last": "B"},{"first": "C", "last": "D"},{"first": "E", "last": "F"}]"""
implicit val NF: Reads[Names] = (
JsPath.read[Seq[Name]]
)(Names.apply _)
<console>:34: error: overloaded method value read with alternatives:
(t: Seq[Name])play.api.libs.json.Reads[Seq[Name]] <and>
(implicit r: play.api.libs.json.Reads[Seq[Name]])play.api.libs.json.Reads[Seq[Name]]
cannot be applied to (Seq[Name] => Names)
JsPath.read[Seq[Name]]

One possiblity is by creating an implicit Reads function:
def readNames: Reads[Names] = new Reads[Names] {
def reads(json: JsValue) = {
json.validate[Seq[Name]].map(succ => Names(succ))
}
}
implicit val NamesFormat = readNames

You don't need to specify Reads for Seq[Name] if you already have one defined for Name.
case class Name(first: String, last: String)
implicit val NameF = Json.format[Name]
val s = """[{"first": "A", "last": "B"},{"first": "C", "last": "D"},{"first": "E", "last": "F"}]"""
scala> Json.parse(s).validate[Seq[Name]]
res2: play.api.libs.json.JsResult[Seq[Name]] = JsSuccess(List(Name(A,B), Name(C,D), Name(E,F)),)

Related

No instance of Reads is available for scala.collection.immutable.List in the implicit scope

I have nested case classes to deserialize a json object into object "C". THe json looks like this:
val jsonResp = {
"parent":{
"children": [
{"name": "a",
"house": "blue"}
{"name": "b",
"house": "green"}
]
}
}
I have nested class to deserialize the values.
case class Parent(children: Children)
case class Children(children: List[Child])
case class Child(name: String, house: String)
I am trying to get the "child" object at 0th index here:
val parent = jsonResp.as[Parent](Json.format[Parent])
val childrenRespObj = jsonResp.as[Children](Json.format[Children])
val child1 = Child(ChildrenRespObj.children.head.name, Child(ChildrenRespObj.children.head.house)
Encountered error: No instance of Reads is available for scala.collection.immutable.List in the implicit scope.
Since "Children" has List[Child] as a parameter then why its throwing the error? How do I resolve this? Thanks.
Json.format needs the dependency Format's to be able to create the Format for dependent type.
You need to make sure that the dependency Format's are available as implicit in the scope where the dependent Format is being created by Json.format[...].
import play.api.libs.json._
object CustomJsonImplicits {
implicit val childFormat = Json.format[Child]
implicit val childrenFormat = Json.format[Children]
implicit val parentFormat = Json.format[Parent]
}
import CustomJsonImplicits._
val parent = jsonResp.as[Parent]
val childAtZerothIndex = parent.children.children(0)
Edit
I just realised that your case classes are kind of wrong for this json. You have to keep in mind that the auto generated Format instances are very particular about json structure. Modifying for correctness.
import play.api.libs.json._
// it is good practice to define case classes as final
// but this code will work the same even without final
final case class Response(parent: Parent)
final case class Parent(children: List[Child])
final case class Child(name: String, house: String)
object CustomJsonImplicits {
implicit val childFormat = Json.format[Child]
implicit val parentFormat = Json.format[Parent]
implicit val responseFormat = Json.format[Response]
}
val jsonResp =
s"""|{
| "parent": {
| "children": [
| { "name": "a", "house": "blue" },
| { "name": "b", "house": "green" }
| ]
| }
|}""".stripMargin
import CustomJsonImplicits._
val jsValue = Json.parse(jsonResp)
val response = jsValue.as[Response]
println(response)
val childAtZerothIndex = response.parent.children(0)
println(childAtZerothIndex)
Also, you can try running this code as a Scastie snippet at - https://scastie.scala-lang.org/sarveshseri/dfBs6PvOT8KDTV4ITdTByA/9

No Json deserializer found for type List Option

How can I use the play json OFormat macro to get a list of option?
val text = """[{"name": "John", "age": 30}, null, {"name": "Steve", "age": 34}]"""
import play.api.libs.json.Json
case class Person(name: String, age: Int)
implicit val personFormat = Json.format[Person]
val data = Json.parse(text).validate[List[Option[Person]]]
// Error: No Json deserializer found for type List[Option[Person]]. Try to implement an implicit Reads or Format for this type.
I am doing as follows, as a workaround:
val data = Json.parse(text).as[Array[JsValue]].toList.map {
case JsNull => None
case x => Some(x.validate[Person].get)
}
println(data)
// List(Some(Person(John,30)), None, Some(Person(Steve,34)))
How do I achieve the same without this workaround, using only the OFormat macro?
Not sure that it is possible directly, but could be done so for example (used this answer):
val text = """[{"name": "John", "age": 30}, null, {"name": "Steve", "age": 34}]"""
import play.api.libs.json._
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]) = o match {
case Some(t) ⇒ implicitly[Writes[T]].writes(t)
case None ⇒ JsNull
}
}
case class Person(name: String, age: Int)
implicit val personFormat= {
implicit val f = Json.format[Person]
implicitly[Format[Option[Person]]]
}
val data = Json.parse(text).validate[List[Option[Person]]] // JsSuccess(List(Some(Person(John,30)), None, Some(Person(Steve,34))),)
Json.toJson(data.get) // [{"name":"John","age":30},null,{"name":"Steve","age":34}]
it just cannot translate by its own Reads[Person] -> Reads[Option[Person]] -> Reads[List[Option[Person]]]. I do help to get Reads[Option[Person]] with helper general method. Probably, analogue method is available in play lib..

Scala loop list of object

I have a list of JsObject like:
[{
"a":"test1",
"b": 2,
"c": 5,
"errors": "Error example"
}]
I would like to get something like this a:List[Option[String]], b: List[Option[Int]] and so on. I need an option since not all the fields are alway present.
My code is:
jsObjList.map(js => {
val a = (js \ "a").asOpt[String]
val b = (js \ "b").asOpt[Int]
val c = (js \ "c").asOpt[Int]
val er= (js \ "errors").asOpt[String]
(a, b, er)
})
I read about unzip and unzip3 but I haven't found a generic function.
P.S. I am using Scala Play for the json parsing
Thanks for your help!
Class to extract values from raw JSON.
case class Foo(a: Option[String], b: Option[Int], c: Option[Int],errors: Option[String])
object Foo {
// Automatically generate json reader and writer for the class Foo
implicit val format = Json.format[Foo]
}
Keeping the implicit value in companion object of Foo will make the Scala to pick up the implicit when required automatically.
Code to parse JSON into list of case class instances
payload.validate[List[Foo]]
Use validateOpt in case you expect any parse error
payload.validateOpt[List[Foo]]
Scala REPL
scala> :paste
// Entering paste mode (ctrl-D to finish)
val str = """
[{
"a":"test1",
"b": 2,
"c": 5,
"errors": "Error example"
}]
"""
// Exiting paste mode, now interpreting.
str: String =
"
[{
"a":"test1",
"b": 2,
"c": 5,
"errors": "Error example"
}]
"
scala> val payload = Json.parse(str)
payload: play.api.libs.json.JsValue = [{"a":"test1","b":2,"c":5,"errors":"Error example"}]
scala> case class Foo(a: Option[String], b: Option[Int], c: Option[Int],errors: Option[String])
defined class Foo
scala> implicit val format = Json.format[Foo]
format: play.api.libs.json.OFormat[Foo] = play.api.libs.json.OFormat$$anon$1#53a0b0a3
scala> payload.validate[List[Foo]]
res5: play.api.libs.json.JsResult[List[Foo]] = JsSuccess(List(Foo(Some(test1),Some(2),Some(5),Some(Error example))),)
You can parse JSON as a Scala case class with a companion object containing a special val called implicit val format = Json.format[*your class*].
Here's an example similar to yours:
import play.api.libs.json.Json
val body =
"""{
| "a":"my string",
| "b": 1,
| "c": 2
|}
""".stripMargin
val body2 =
"""{
| "a":"my string",
| "c": 5
|}
""".stripMargin
case class MyClass(a: Option[String], b: Option[Int], c: Option[Int])
object MyClass {
implicit val format = Json.format[MyClass]
}
Using this, calling Json.parse(body).as[MyClass] gives:
res0: MyClass = MyClass(Some(my string),Some(2),Some(5))
Calling this Json.parse function with missing fields (assuming they are optional), such as Json.parse(body2).as[MyClass] gives:
res1: MyClass = MyClass(Some(my string),None,Some(5))
If one of the missing fields is not Optional, this parse will not work.

Play framework read from json list

So I have a structure in json looking like this:
{
"lst": [
{"name": "foo"},
{"name": "bar"}
]
}
I'm having the hardest time converting this to a list of case classes. I'm sure I'm missing something completely obvious...
I have tried this:
case class Person(name: String)
implicit val personReads: Reads[Email] = (__ \\ "name").read[String](Person)
// endpoint
def person = Action { request =>
val person = request.body.asJson.get.as[Seq[Person]]
}
which doesn't compile since read doesn't return a FunctionBuilder which means I can't apply the path to Person
Adding a new parameter does compile (changing the json and case class accordingly):
case class Person(name: String, age: String)
implicit val personReads: Reads[Email] = (
(__ \\ "name").read[String]) and
(__ \\ "age").read[Int](Person)
but throws an exception Execution exception[[JsResultException: JsResultException(errors:List((,List(ValidationError(List(error.expected.jsarray),WrappedArray())))))]] supposedly because it expects a list.
So I tried adding this:
implicit val personsReads: Reads[Seq[Person]] = (__ \ "lst").read[Seq[Person]]
which then throws a NullPointer.
In the end I just want a Seq[Person].
Can anyone point me in the right direction, I'm completely lost to what I'm expected to do here...
You can do the following instead of giving reads and writes explicitly.
import play.api.json.Json
case class Person(name: String)
object Person {
implicit val personFormat = Json.format[Person]
}
case class Persons(lst: List[Person])
object Persons {
implicit val personsFormat = Json.format[Persons]
}
Now take the json string lets say jsonStr
Json.parse(jsonStr).validate[Persons] match {
case JsSuccess(persons, _) => println(persons)
case JsError(_) => println("parsing failed")
}
Scala REPL
scala> import play.api.libs.json._
import play.api.libs.json._
scala> val str = """{
| "lst": [
| {"name": "foo"},
| {"name": "bar"}
| ]
| }""".stripMargin
str: String =
{
"lst": [
{"name": "foo"},
{"name": "bar"}
]
}
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Person(name: String)
object Person {
implicit val personFormat = Json.format[Person]
}
case class Persons(lst: List[Person])
object Persons {
implicit val personsFormat = Json.format[Persons]
}
// Exiting paste mode, now interpreting.
defined class Person
defined object Person
defined class Persons
defined object Persons
scala> val jsonStr = str
jsonStr: String =
{
"lst": [
{"name": "foo"},
{"name": "bar"}
]
}
scala> :paste
// Entering paste mode (ctrl-D to finish)
Json.parse(jsonStr).validate[Persons] match {
case JsSuccess(persons, _) => println(persons)
case JsError(_) => println("parsing failed")
}
// Exiting paste mode, now interpreting.
Persons(List(Person(foo), Person(bar)))
Now when you change your Person case class and add age field.
case class Person(name: String, age: Int)
object Person {
implicit val personFormat = Json.format[Person]
}
Ensure that the json you are trying to parse contains both name and age. If you have only name then you will get parsing error.

Deserialize json without name

I'm Scala na json4s to consume json. To deserialize I'm calling org.json4s.native.JsonMethods.parse and ExtractableJsonAstNode.extract method.
This is a part of json file:
"": {
"atribute1": "v1",
"instanceId": "i",
},
It contains attribute without name.
What should be field name in case class to successfully deserialize attributes?
I think you can't parse such json into case class. Unless you do a custom deserializer for it and then you can decide it yourself.
import org.json4s.{JValue, CustomSerializer, DefaultFormats}
import org.json4s.native.JsonMethods
import org.json4s.JsonDSL._
import org.json4s._
case class Outer(value: Inner, other: String)
case class Inner(atribute1: String, instanceId: String)
object Formats extends DefaultFormats {
val outerSerializer = new CustomSerializer[Outer](implicit format ⇒ (
{ case j: JValue ⇒ Outer(
(j \ "").extract[Inner],
(j \ "other").extract[String]
)},
{ case a: Outer ⇒
("" → Extraction.decompose(a.value)) ~
("other" → a.other)
})
)
override val customSerializers = List(outerSerializer)
}
implicit val formats = Formats
val json = """
{
"": {
"atribute1": "v1",
"instanceId": "i",
},
"other": "1"
}
"""
JsonMethods.parse(json).extract[Outer]