Разбираем json и по значению возвращаем список ключей - PullRequest
0 голосов
/ 29 марта 2020

Scala Эксперты, нужна ваша помощь. задача состоит в том, чтобы проанализировать этот json и вернуть список ModelIds для магазина 'livingstone', который имеет 'organi c' = true.

В этом случае только RT001 имеет значение c органики как истинное. Также нужна помощь, как отфильтровать на основе магазина = живой камень и органический = истина.

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))

Ответы [ 2 ]

2 голосов
/ 29 марта 2020

При обработке JSON лучше всего преобразовать всю структуру в Scala, а затем обработать Scala, а не непосредственно обрабатывать JSON.

Так что создайте класс Response и извлеките это в одной операции, а затем обработайте поле 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.

1 голос
/ 29 марта 2020

Итак, как уже было предложено в разделе комментариев, вы можете перейти к библиотеке circe вместо платформы Lift, потому что это гораздо более современное и широко используемое решение.

Что вы нужно сделать - объявить структуры, например case class, которые представляют ваш json. Это не рекомендуемый подход, работать над необработанным JSON - эмпирическим правилом - анализировать его в какую-то значимую структуру и затем работать с ней.

Наряду с декларацией структуры вы также Encoder и Decoder скажем, в «нестандартных» случаях - например, Boolean, что составляет String в случае поля 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))

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...