1

My question is a kind-of extension to this and this one too.

My JSON looks like:

{
    "id": "id1",
    "results": [
        {
            "exceed_size": "yes",
            "rows_count": 1001,
            "runtime_seconds": 0.02199999988079071,
            "columns": [
                "COL_1",
                "COL_2",
                "COL_3",
                "COL_4",
                "COL_5",
                "COL_6",
                "COL_7",
                "COL_8",
                "COL_9"
            ],
            "columns_type": [
                "number",
                "string",
                "string",
                "string",
                "number",
                "time",
                "time",
                "number",
                "string"
            ],
            "limit": 1000,
            "index": 0,
            "rows": [
                [
                    "9",
                    " C68894",
                    "                                                                                                    ",
                    "",
                    "0",
                    "2018-05-02 03:13:00.0",
                    "2017-12-02 22:32:00.0",
                    "",
                    "Approved  "
                ],
                [
                    "65",
                    "325806   ",
                    "msm                                                                             ",
                    "                 ",
                    "2",
                    "2018-05-02 03:13:00.0",
                    "2018-07-06 06:00:00.0",
                    "13",
                    "Approved  "
                ],
                ...
            ]
        },
        ...
    ]
}

I'm using Play Framework provided JSON Library for JSON Parsing.

If you have a look at the rows value, it is a JsArray of a JsArray of string values. I have been trying to convert rows into a List of objects of a case class, where my case class would look like:

case class Rows(col_1: String, col_2: String, ... , col_9: String)

I had tried to do something like:

val rows = (response \\ "rows").head.as[List[List[(String, String, ... , String)]]].flatten

Trying This way threw an error, which I was sure of won't work. How do I convert such a JsArray into a List of objects of a case class?

EDIT 1:

As @MilanRegmi suggested, I tried:

implicit val jsonFormat: Format[Rows] = Json.format[Rows]

val emails = (response \ "results" \ "rows").as[JsArray]
            .value.map(j => j.validate[Rows].get)

Trying this resulted in:

Exception in thread "main" play.api.libs.json.JsResultException: JsResultException(errors:List((,List(JsonValidationError(List([{"exceed_size":"yes","rows_count":1001,"runtime_seconds":0.01600000075995922,"columns":["COL_1","COL_2","COL_3","COL_4","COL_5","COL_6","COL_7","COL_8","COL_9"],"columns_type":["number","string","string","string","number","time","time","number","string"],"limit":1000,"index":0,"rows":[["9"," C68894","","","0","2018-05-02 03:13:00.0","2017-12-02 22:32:00.0","","Approved  "],["65","325806   ","msm                                                                             ","                 ","2","2018-05-02 03:13:00.0","2018-07-06 06:00:00.0","13","Approved  "],...]}] is not an object),WrappedArray())))))
    at play.api.libs.json.JsReadable$$anonfun$2.apply(JsReadable.scala:25)
    at play.api.libs.json.JsReadable$$anonfun$2.apply(JsReadable.scala:25)
    at play.api.libs.json.JsError.fold(JsResult.scala:64)
    at play.api.libs.json.JsReadable$class.as(JsReadable.scala:23)
    at play.api.libs.json.JsUndefined.as(JsLookup.scala:181)
    at com.cmdwldap.restapi.User.getEntitlementUserData(User.scala:150)
    at com.cmdwldap.restapi.User$.main(User.scala:168)
    at com.cmdwldap.restapi.User.main(User.scala)

PS: Line 150 corresponds to the place where val emails is mentioned.

1
  • Are you looking for something like this? (Json.parse(jsonString) \ "results" \ 0 \ "rows").validate[List[List[String]]] Commented Feb 22, 2021 at 13:46

1 Answer 1

1

Try this:

val rows: Seq[Rows] = (json \ "result" \ "rows").as[JsArray].value.map(j => j.validate[Rows].get)

Update:

After going through your question for several times. I got your question now. You want to convert List[List[String] into CaseClass.

First, you cannot convert Array of List of String into a case class directly. So, you need to convert Array[String] to JsObject where key should be fieldName of the class. After that we can fetch fieldName using reflection. Then, we need to create JsObject to match caseClass and List[String] using the fieldName.

Pro-grammatically, above explained scenario can be solve in the following way:

 case class Rows(col1: Option[String] = None,
               col2: Option[String] = None,
               col3: Option[String] = None,
               col4: Option[String] = None,
               col5: Option[String] = None,
               col6: Option[String] = None,
               col7: Option[String] = None,
               col8: Option[String] = None,
               col9: Option[String] = None)

implicit val reads = Json.reads[Rows]

This is our case class and the implicits reads. Now, the above explained part is below:

import scala.reflect.runtime.universe._

def classAccessors[T: TypeTag]: List[MethodSymbol] = typeOf[T].members.collect {
    case m: MethodSymbol if m.isCaseAccessor => m
  }.toList


 val rowFieldNames = classAccessors[Rows].map(k => k.name.toString)
 val results =  (json \ "results").as[JsArray].value.flatMap{
    r => (r \ "rows").as[JsArray].value
  }.map{row =>
    val rowArray = row.as[JsArray]
    val rowArraySeq = rowArray.value.map(_.as[JsString]).map(_.value)
    val map = rowArraySeq.foldLeft(Map.empty[String, JsValue]){
      (r, c) =>
        val indexOfCurrentValue = rowArraySeq.indexOf(c)
        val fieldName = rowFieldNames(indexOfCurrentValue)
        r.+((fieldName, JsString(c)))
    }
    val rowJsObject = JsObject(map)
    Json.toJson(rowJsObject)
        }.toList
Sign up to request clarification or add additional context in comments.

13 Comments

Error: No implicits found for parameter rds: Reads[Rows]
You have missed implicits reads and writes. Add implicit formatter like this: implicit val jsonFormat: Format[Rows] = Json.format[Rows]
Please check EDIT 1
It seems like you have provided an invalid json. Please check your json first and make sure it is valid json.
It is a valid JSON.. :(
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.