问题
I need help to parse a json string into scala class using playJson
I wrote a formatter but I don't know how to handle the nested arrays.
Where Document Case class is
case class Document(content: String, score: Double, size: Int, path:String)
and formatter
implicit val similarHashFormatter: Format[SimilarHash] = (
((__ \ "hits" \ "hits" \\ "fields")(0) \ "content_hash")(0).format[String] and
(__ \ "hits" \ "hits" \\ "_score").format[Double] and
((__ \ "hits" \ "hits" \\ "fields")(0) \ "ast_size")(0).format[Int] and
((__ \ "hits" \ "hits" \\ "fields")(0) \ "path")(0).format[String]
) (SimilarHash.apply, unlift(SimilarHash.unapply))
This is my source json
{
"hits": {
"hits": [
{
"score": 1.5204661,
"fields": {
"size": [
557645
],
"path": [
"/user/ubuntu/app
],
"content": [
"images"
]
}
},
{
"score": 1.5199462,
"fields": {
"size": [
556835
],
"path": [
"/user/ubuntu/app
],
"content": [
"documents"
]
}
}
]
}
}
Any idea ?
回答1:
You could do this by creating custom Reads
separately for each field as follow:
import play.api.libs.json._
import play.api.libs.functional.syntax._
case class Document(content: String, score: Double, size: Int, path:String)
val jsonString = """{
"hits": {
"hits": [
{
"score": 1.5204661,
"fields": {
"size": [
557645
],
"path": [
"/user/ubuntu/app"
],
"content": [
"images"
]
}
},
{
"score": 1.5199462,
"fields": {
"size": [
556835
],
"path": [
"/user/ubuntu/app"
],
"content": [
"documents"
]
}
}
]
}
}"""
val playJson = Json.parse(jsonString)
val contentReads = new Reads[String] {
override def reads(json: JsValue): JsResult[String] = json \ "hits" match {
case JsDefined(o: JsObject) =>
o \ "hits" match {
case JsDefined(arr: JsArray) =>
arr.value.head \ "fields" match {
case JsDefined(fieldObj: JsObject) =>
fieldObj \ "content" match {
case JsDefined(contentArr: JsArray) =>
JsSuccess(Json.stringify(contentArr.value.head))
case _ => JsError( """Can't read hits \ hits \ fields \ content""")
}
case _ => JsError( """Can't read hits \ hits \ fields""")
case _ => JsError( """Can't read hits \ hits""")
}
case _ => JsError("Can't read hits")
}
}
}
val sizeReads = new Reads[Int] {
override def reads(json: JsValue): JsResult[Int] = json \ "hits" match {
case JsDefined(o: JsObject) =>
o \ "hits" match {
case JsDefined(arr: JsArray) =>
arr.value.head \ "fields" match {
case JsDefined(fieldObj: JsObject) =>
fieldObj \ "size" match {
case JsDefined(contentArr: JsArray) =>
JsSuccess(Json.stringify(contentArr.value.head).toInt)
case _ => JsError("""Can't read hits \ hits \ fields \ size""")
}
case _ => JsError("""Can't read hits \ hits \ fields""")
}
case _ => JsError("""Can't read hits \ hits""")
}
case _ => JsError("Can't read hits")
}
}
val scoreReads = new Reads[Double] {
override def reads(json: JsValue): JsResult[Double] = json \ "hits" match {
case JsDefined(o: JsObject) =>
o \ "hits" match {
case JsDefined(arr: JsArray) =>
arr.value.head \ "score" match {
case JsDefined(score: JsValue) =>
JsSuccess(Json.stringify(score).toDouble)
case _ => JsError("""Can't read hits \ hits \ score""")
}
case _ => JsError("""Can't read hits \ hits""")
}
case _ => JsError("Can't read hits")
}
}
val pathReads = new Reads[String] {
override def reads(json: JsValue): JsResult[String] = json \ "hits" match {
case JsDefined(o: JsObject) =>
o \ "hits" match {
case JsDefined(arr: JsArray) =>
arr.value.head \ "fields" match {
case JsDefined(fieldObj: JsObject) =>
fieldObj \ "path" match {
case JsDefined(contentArr: JsArray) =>
JsSuccess(Json.stringify(contentArr.value.head))
case _ => JsError("""Can't read hits \ hits \ fields \ path""")
}
case _ => JsError("""Can't read hits \ hits \ fields""")
}
case _ => JsError("""Can't read hits \ hits""")
}
case _ => JsError("Can't read hits")
}
}
implicit val documentReads: Reads[Document] = (
contentReads and
scoreReads and
sizeReads and
pathReads
)(Document.apply _)
val document = playJson.validate[Document].get
//result: Document("images",1.5204661,557645,"/user/ubuntu/app")
回答2:
I figured out the solution based on oblivion's comment but without creating multiple reads.
implicit val docReader: Reads[Document] = (
(__ \ "fields" \ "content")(0).read[String] and
(__ \ "_score").read[Double] and
((__ \ "fields") \ "size")(0).read[Int] and
((__ \ "fields") \ "path")(0).read[String]
) (Document.apply _)
implicit val docsReader: Reads[Documents] = (
(__ \ "hits" \ "max_score").read[Double] and
(__ \ "hits" \ "hits").read[Seq[Document]]
) (Documents.apply _)
... and finally
val response = Json.parse(inputStream).asOpt[Documents]
来源:https://stackoverflow.com/questions/42168817/scala-convert-string-to-json-using-play-json