【问题标题】:Convert a JsArray of JsArray into into Case class将 JsArray 的 JsArray 转换为 Case 类
【发布时间】:2019-08-15 20:56:08
【问题描述】:

我的问题是thisthis 的一种扩展。

我的 JSON 看起来像:

{
    "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  "
                ],
                ...
            ]
        },
        ...
    ]
}

我正在使用 Play Framework 提供的 JSON 库进行 JSON 解析。

如果您查看rows 值,它是JsArrayJsArray 字符串值。我一直在尝试将rows 转换为案例类的对象列表,我的案例类如下所示:

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

我曾尝试做类似的事情:

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

尝试这种方式会引发错误,我确信这不会起作用。如何将这样的JsArray 转换为案例类的对象列表?

编辑 1:

正如@MilanRegmi 建议的那样,我试过了:

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

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

尝试这样做的结果是:

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:第150行对应提到val emails的地方。

【问题讨论】:

  • 您在寻找这样的东西吗? (Json.parse(jsonString) \ "results" \ 0 \ "rows").validate[List[List[String]]]

标签: scala playframework jsvalue


【解决方案1】:

试试这个:

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

更新:

在多次回答您的问题后。我现在收到你的问题了。您想将List[List[String] 转换为CaseClass

首先,您不能将 Array of List of String 直接转换为 case 类。因此,您需要将Array[String] 转换为JsObject,其中key 应该是类的fieldName。之后我们可以使用反射获取fieldName。然后,我们需要使用fieldName 创建JsObject 来匹配caseClassList[String]

从程序上讲,上述场景可以通过以下方式解决:

 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]

这是我们的案例类和隐含的reads。现在,上面解释的部分如下:

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

【讨论】:

  • 错误:No implicits found for parameter rds: Reads[Rows]
  • 你错过了隐式读写。像这样添加隐式格式化程序:implicit val jsonFormat: Format[Rows] = Json.format[Rows]
  • 请检查 EDIT 1
  • 您提供的 json 似乎无效。请先检查您的 json 并确保它是有效的 json。
  • 这是一个有效的 JSON.. :(
猜你喜欢
  • 1970-01-01
  • 2014-03-11
  • 2013-10-25
  • 1970-01-01
  • 2018-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多