解析 json 并基于值 return 键列表

Parse json and based on value return a list of keys

Scala 专家,需要您的帮助。任务是解析 json 和 return 商店 'livingstone' 的 ModelId 列表,其中 'organic' = true。

在这种情况下,只有 RT001 具有有机价值作为 true.Please 帮助。

注意:尝试使用现有的 liftweb 库。

package Exercises


import net.liftweb.json.{DefaultFormats, _}


object test1 extends App {

  val json_response =
    """{
  "requestId": "91ee60d5f1b45e#316",
  "error": null,
  "errorMessages": [
  ],
  "entries": [

  {

    "modelId":"RT001",
    "sku": "SKU-ASC001",
    "store": "livingstone",
    "ttlInSeconds": 8000,
    "metadata": {
      "manufactured_date": "2019-01-22T01:25Z",
      "organic": "true"
    }
  },
  {

    "modelId":"RT002",
    "sku": "SKU-ASC002",
    "store": "livingstone",
    "ttlInSeconds": 8000,
    "metadata": {
      "manufactured_date": "2019-10-03T01:25Z",
      "organic": "false"
    }
  }

  ] }"""


  val json = parse(json_response)

  implicit val formats = DefaultFormats

  val elements = (json \ "entries").children


  for (subs <- elements) {
    val m = subs.extract[Subs]
    println(s"Subscriptions: ${m.modelId}, ${m.store}")
    println(" k,v: " + m.metadata.exists(_ == ("organic", "true")))
  }

  case class Subs(modelId: String, store: String, metadata: Map[String, String])


}

获取错误。还需要帮助如何根据 store=living stone 和 organic=true 进行过滤。

Exception in thread "main" net.liftweb.json.MappingException: No usable value for modelId
Do not know how to convert JArray(List(JString(RT001), JString(RT002))) into class java.lang.String

在专家帮助下的最终工作代码:

  val json = parse(json_response)

  implicit val formats = DefaultFormats


  case class Sales(modelId: String, sku: String, store: String, ttlInSeconds: Int, metadata: Map[String, String])

  case class Response(entries: List[Sales])

  val json1 = parse(json_response)
  val response = json.extract[Response]

  val subs = response.entries.filter { e =>
    e.store == "livingstone" &&
      e.metadata.get("organic").contains("true")

  }

  subs.foreach(x=>println(x.modelId))

因此,正如评论部分所建议的那样,您可以继续使用 circe 库而不是 Lift 框架,因为它是更现代且使用更广泛的解决方案。

您需要做什么 - 声明结构,例如 case class 代表您的 json。这是不推荐的方法,对原始 JSON 进行操作 - 经验法则 - 将其解析为一些有意义的结构,然后使用它。

连同结构声明,你还 EncoderDecoder 比方说 "non standard" 案例 - 比如 BooleanString organic 字段。

在您的情况下,代码可能如下所示:

object App {
  def main(args: Array[String]): Unit = {
    import io.circe._, io.circe.generic.semiauto._, io.circe.generic.auto._, io.circe.parser._

    /**
     * General purpose response wrapper. Entries type might differ, as I can suppose, that's why it is generic.
     * 'errorMessages' - since it is empty array in example, I can only guess about exact type. For sake of example
     * let's say it is strings. And same for 'error' field.
     */
    case class Response[E](requestId: String, error: Option[String], errorMessages: List[String], entries: List[E])
    object Response {
      implicit def decoder[E](implicit d: Decoder[E]): Decoder[Response[E]] = deriveDecoder[Response[E]]
      implicit def encoder[E](implicit e: Encoder[E]): Encoder[Response[E]] = deriveEncoder[Response[E]]
    }

    case class Product(modelId: String, sku: String, store: String, ttlInSeconds: Int, metadata: ProductMetadata)

    case class ProductMetadata(manufactured_date: ZonedDateTime, organic: Boolean)
    object ProductMetadata {
      // Boolean codec required - because `organic` is a string in JSON, which has boolean type
      implicit val booleanDecoder: Decoder[Boolean] = Decoder[String].emapTry(value => Try(value.toBoolean))
      implicit val booleanEncoder: Encoder[Boolean] = Encoder[String].contramap(_.toString)

      implicit val decoder: Decoder[ProductMetadata] = deriveDecoder[ProductMetadata]
      implicit def encoder: Encoder[ProductMetadata] = deriveEncoder[ProductMetadata]
    }

    val json =
      s"""
         |{
         |   "requestId":"91ee60d5f1b45e#316",
         |   "error":null,
         |   "errorMessages":[
         |
         |   ],
         |   "entries":[
         |      {
         |         "modelId":"RT001",
         |         "sku":"SKU-ASC001",
         |         "store":"livingstone",
         |         "ttlInSeconds":8000,
         |         "metadata":{
         |            "manufactured_date":"2019-01-22T01:25Z",
         |            "organic":"true"
         |         }
         |      },
         |      {
         |         "modelId":"RT002",
         |         "sku":"SKU-ASC002",
         |         "store":"livingstone",
         |         "ttlInSeconds":8000,
         |         "metadata":{
         |            "manufactured_date":"2019-10-03T01:25Z",
         |            "organic":"false"
         |         }
         |      }
         |   ]
         |}
         |""".stripMargin

    val parseResult: Either[Error, List[String]] =
      for {
        parsedJson <- parse(json)
        response <- parsedJson.as[Response[Product]]
      } yield  {
        response.entries.collect {
          case Product(modelId, _, "livingstone", _, ProductMetadata(_, true)) => modelId
        }
      }
    println(parseResult)
  }

这将产生下一个结果

Right(List(RT001))

希望对您有所帮助!

在处理JSON时最好将整个结构转为Scala再处理Scala,而不是直接处理JSON.

因此创建一个 Response class 并在单个操作中提取它,然后根据需要处理 entries 字段。

这是一些完全未经测试的代码:

case class Response(entries: List[Subs])

val json = parse(json_response)
val response = json.extract[Response]

val subs = response.entries.filter{e =>
    e.store == "livingstone" &&
    e.metadata.get("organic").contains("true")
  }

请注意,这应该适用于任何允许您从 JSON.

中提取 class 的 JSON 库