I have the following Json as var dataObject ={"files": ["code.R", "data.cv", "input.txt"]}.
I am posting this json as a body from the client side and I want to parse the Json and read these files names in the server side in play scala.
Please help
Because you have only one field, you can't use json combinators,
But you can do as follow:
case class Selection(files:List[String])
object Selection{
implicit val selectionReads = (__ \ 'files).read[List[String]].map{ l => Selection(l) }
implicit val selectionWrites = (__ \ 'files).write[List[String]].contramap { (selection: Selection) => selection.files}
//You can replace the above 2 lines with this line - depends on you.
implicit val selectionFormat: Format[Selection] = (__ \ 'files).format[List[String]].inmap(files => Selection(files), (selection: Selection) => selection.files)
}
Make sure you import:
import play.api.libs.functional.syntax._
This is the documentation: https://www.playframework.com/documentation/2.5.x/ScalaJson
And the solution is simple:
import play.api.libs.json._
val json: JsValue = Json.parse("{ "files": ["code.R","data.csv","input.txt"] }")
val files = (json \ "files").get
Related
I am trying to update a json value present within a json using Scala play framework.Instead of updating the value it is appending the value.
val newJsonString = """{"P123": 25}"""
val jsonStringAsJsValue = Json.parse("""{"counter_holders": {"Peter": 25}}""")
//jsonStringAsJsValue: play.api.libs.json.JsValue = {"counter_holders":{"Peter":25}}
val jsonTransformer = (__ \"counter_holders" ).json.update(__.read[JsValue].map{o => Json.parse(newJsonString)})
jsonStringAsJsValue.transform(jsonTransformer).get.as[JsValue]
//Now getting this jsvalue
//play.api.libs.json.JsValue = {"counter_holders":{"Peter":25,"P123":25}}
//But need this jsvalue
//play.api.libs.json.JsValue = {"counter_holders":{"P123":25}}
Any help on this will be really nice.
Quoting from the update method docs:
(__ \ 'key).json.update(reads) is the most complex Reads[JsObject]
but the most powerful:
copies the whole JsValue => A
applies the passed Reads[A] on JsValue => B
deep merges both JsValues (A ++ B) so B overwrites A identical branches Please note that if you have prune a branch in B, it is still
in A so you'll see it in the result Example:
{{{ val js = Json.obj("key1" -> "value1", "key2" -> "value2")
js.validate(__.json.update((__ \ 'key3).json.put(JsString("value3"))))
=> JsSuccess({"key1":"value1","key2":"value2","key3":"value3"},) }}}
Therefore the behaviour you see is as expected. If you want to take that approach, of updating using the path, you can use the method prune. For example you can do:
val newJsonString = """{"P123": 25}"""
val jsonStringAsJsValue = Json.parse("""{"counter_holders": {"Peter": 25}}""")
//jsonStringAsJsValue: play.api.libs.json.JsValue = {"counter_holders":{"Peter":25}}
val jsonTransformer = (__ \"counter_holders" ).json
.update(__.read[JsValue].map{o => Json.parse(newJsonString)})
val jsonTransformerDelete = (__ \"counter_holders" \ "Peter" ).json.prune
jsonStringAsJsValue.transform(jsonTransformer).flatMap(_.transform(jsonTransformerDelete)) match {
case JsSuccess(value, _) =>
println(value)
case JsError(errors) =>
println(errors)
}
which will produce the wanted behaviour. You can find it in scastie.
I'd like to use PlayJson to only validate multiple fields of some json and not map it to a custom object. I Only care about the Yes Or No answer to the validation criteria. Is it possible to use PlayJson in that way? So far I have something like,
val json = .....
val reads = (JsPath \ "foo").read[String](min(5)) and
(JsPath \ "bar").read[String](max(10))
json.validate["I ONLY WANT TO VALIDATE NOT MAP"](reads) match {
case s: JsSuccess => true
case e: JsError => false
}
Thank you Stack Overflow community.
Instead of deserialising to a case class model via Reads[MyModel] we can deserialise to a tuple via Reads[(String, String)] like so
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
val reads = (
(JsPath \ "foo").read[String](minLength[String](5)) and
(JsPath \ "bar").read[String](minLength[String](10))
).tupled
val json = Json.parse(
"""
|{
| "foo": "abcde",
| "bar": "woohoowoohoo",
| "zar": 42
|}
|""".stripMargin)
json.validate(reads).isSuccess
which outputs
res0: Boolean = true
Note how we called tupled method when creating the reader, and isSuccess to get a boolean out of validation process.
https://scalafiddle.io/sf/JBjdt2Y/0
I am trying to use play.api.lib.json to convert a json to my object. But then this happend...
case class Foo(foo:Option[Map[String,String]])
case class Bar(bar:String,foo:Foo)
def barJsonToModel(foobarJson:JsValue):Bar = {
implicit val fooReads: Reads[Foo] = (
( JsPath \ "foo" ).readNullable[Map[String,String]]
)(Foo.apply _)
}
Expression of type Reads[Option[Map[String,String]]] doesn't comfort to expect type Reads[Foo]
You are using the functional syntax of play-json, but so an import is missing before:
import play.api.libs.functional.syntax._
I am experimenting with the json4s library (based on lift-json). One of the things I would like to do is to parse a JSON string into an AST, and then manipulate it.
For example, I would like to upsert a field (insert the field into the AST if it does not exist, or update its value if it does).
I have not been able to find how to do it in the documentation. Experimenting with the available methods, I have come up with the following, which works, but feels clumsy.
import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.native.JsonMethods._
object TestJson {
implicit val formats = DefaultFormats
def main(args: Array[String]): Unit = {
val json = """{"foo":1, "bar":{"foo":2}}"""
val ast = parse(json).asInstanceOf[JObject]
println( upsertField(ast, ("foo" -> "3")) )
println( upsertField(ast, ("foobar" -> "3")) )
}
def upsertField(src:JObject, fld:JField): JValue = {
if(src \ fld._1 == JNothing){
src ~ fld
}
else{
src.replace(List(fld._1), fld._2)
}
}
}
I dislike it for many reasons:
Having to explicitly cast the results of parse(json) to JObject
The result of the upsertField function is a JValue, which I will have to recast if I want to manipulate the object further
The upsertField function just feels very unelegant
It does not work for fields that are not at the top level of the hierarchy
Is there a better way to transform the AST?
EDIT: as a workaround to the problem, I have managed to convert my JSON to Scala regular classes, and manipulate them with lenses (Using Lenses on Scala Regular Classes)
There is the merge function which creates or overrides a field. You can also update fields that are not at the root level of the tree.
import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.jackson.JsonMethods._
object mergeJson extends App {
val json =
"""
|{
| "foo":1,
| "bar": {
| "foo": 2
| }
|}
|""".stripMargin
val ast = parse(json)
val updated = ast merge (("foo", 3) ~ ("bar", ("fnord", 5)))
println(pretty(updated))
// {
// "foo" : 3,
// "bar" : {
// "foo" : 2,
// "fnord" : 5
// }
// }
}
Let me also give you the SON of JSON version:
import nl.typeset.sonofjson._
val json = parse("""{ "foo" : 1, "bar" : { "foo" : 2 } }""")
// or, perhaps a little easier
val json = obj(foo = 1, bar = obj(foo = 2))
json.foo = "3"
json.foobar = "3"
When I was implementing some very specific json diff using lift json I used a lot of recursive functions to get to the jpath where I need to modify value, and modified json was constructed when recursion "collapsed". LiftJson is immutable after all. You mentioned lenses as another approach, which is very interesting by itself. But my current favourite is play-json library that is working like a charm in a situation when you need to do json-to-json transformation:
from Mandubian Blog:
val gizmo2gremlin = (
(__ \ 'name).json.put(JsString("gremlin")) and
(__ \ 'description).json.pickBranch(
(__ \ 'size).json.update( of[JsNumber].map{ case JsNumber(size) => JsNumber(size * 3) } ) and
(__ \ 'features).json.put( Json.arr("skinny", "ugly", "evil") ) and
(__ \ 'danger).json.put(JsString("always"))
reduce
) and
(__ \ 'hates).json.copyFrom( (__ \ 'loves).json.pick )
) reduce
Yummy Features: all transformers are combinators that can be mixed together, validation, shapeless support, auto marshaling of case classes with implicit overrides, stand-alone library.
I have following two implicits.
implicit val readObjectIdFormat = new Reads[ObjectId] {
def reads(jv: JsValue): JsResult[ObjectId] = {
JsSuccess(new ObjectId(jv.as[String]))
}
}
implicit val visitorFormat = (
(__ \ "_id").formatOpt[ObjectId] and
(__ \ "visitorId").format[String] and
(__ \ "referralUrl").formatOpt[String] and
(__ \ "ipAddress").formatOpt[String] and
(__ \ "promotionId").format[String])(Visitor)
Though readObjectIdFormat is defined at compile time it keeps complaining following on "(__ \ "_id").formatOpt[ObjectId]" line
No Json formatter found for type org.bson.types.ObjectId. Try to implement an implicit
Format for this type.
versions : Play 2.1-RC2, Scala 2.10
Any idea why it's not recognizing readObjectIdFormat ?
Others gave the good answer, use Format instead.
By the way, you could handle parse errors.
This implementation is working fine for me:
implicit val objectIdFormat: Format[ObjectId] = new Format[ObjectId] {
def reads(json: JsValue) = {
json match {
case jsString: JsString => {
if ( ObjectId.isValid(jsString.value) ) JsSuccess(new ObjectId(jsString.value))
else JsError("Invalid ObjectId")
}
case other => JsError("Can't parse json path as an ObjectId. Json content = " + other.toString())
}
}
def writes(oId: ObjectId): JsValue = {
JsString(oId.toString)
}
}
You are implementing Reads and you need implement Format instead.
implicit val readObjectIdFormat = new Format[ObjectId] {
def reads(jv: JsValue): JsResult[ObjectId] = {
JsSuccess(new ObjectId(jv.as[String]))
}
def writes(o: A): JsValue = JsString(...)
}
Or you need to use the read instead of format (note I assume this works for read, haven't tested it).
implicit val visitorRead = (
(__ \ "_id").readOpt[ObjectId] and
(__ \ "visitorId").read[String] and
(__ \ "referralUrl").readOpt[String] and
(__ \ "ipAddress").readOpt[String] and
(__ \ "promotionId").read[String])(Visitor)
From documentation: Format[T] extends Reads[T] with Writes[T]
Format is a read + write.
Write an implicit writeObjectIdFormat then
implicit val formatObjectIdFormat =
Format(readObjectIdFormat, writeObjectIdFormat)