Scala Parse JSON, который имеет список карт - PullRequest
0 голосов
/ 28 сентября 2018

У меня есть какой-то json, когда я проанализировал, что он возвращает структуру Some (List (Map ...))

Как сопоставить что-то и получить значения

Ниже приведен кодЯ пытался, мне нужно получить все значения карты

 import scala.util.parsing.json._
 val result = JSON.parseFull("[{\"start\":\"starting\",\"test\":123,\"test2\":324,\"end\":\"ending\"}]")


 result match {
   case Some(map: Map[String, Any]) => { println(map) 

   }

   case None => println("Parsing failed")
   case other => println("Unknown data structure: " + other)
 }

, но его печать не соответствует

 Unknown data structure: Some(List(Map(start -> starting, test -> 123, test2 -> 324, end -> ending)))

Ответы [ 3 ]

0 голосов
/ 28 сентября 2018

Как уже указывалось, тип возвращаемого значения на самом деле Option[List[Map[String, Any]]], поэтому вам нужно снять этот флажок.Однако вы не можете сделать это с одним match из-за стирания типа, поэтому вам нужно делать вложенные совпадения, чтобы убедиться, что у вас правильный тип.Это действительно утомительно, поэтому я настоятельно рекомендую использовать что-то вроде Extraction.extract функции в json4s, которая будет пытаться сопоставить ваш JSON с конкретным типом Scala:

type ResultType = List[Map[String, Any]]

def extract(json: JValue)(implicit formats: Formats, mf: Manifest[ResultType]): ResultType =
  Extraction.extract[ResultType](json)

Если вы должны сделать это вручную, это выглядит примерно так:

result match {
  case Some(l: List[_]) =>
    l.headOption match {
      case Some(m) =>
        m match {
          case m: Map[_,_] =>
            m.headOption match {
              case Some(p) =>
                p match {
                  case (_: String, _) =>
                    m.foreach(println(_))
                  case _ => println("Map key was not String")
                }
              case _ => println("Map was empty")
            }
          case _ => println("List did not contain a Map")
        }
      case _ => println("Result List was empty")
    }
  case _ => println("Parsing failed")
}
0 голосов
/ 28 сентября 2018

Из-за стирания типа вы не можете сопоставлять шаблоны на типовых типах.List, Map, Option являются универсальными контейнерами, и во время выполнения компилятор удалит типы этих универсальных контейнеров.например, List[String], String будут удалены, а тип будет List[_].

case Some(map: List[Map[String, Any]]) => println(map) 

В приведенном выше случае, если результат равен val result: Option[Any] = Some(List(12)), то есть 12, типом которого является Int, а не Map[String, Any], компилятор все равно будет сопоставлять result с приведенным выше случаем, дажеон ожидает Map[String, Any]] как тип List.

Итак, что происходит?

Все из-за стирания типа .Компилятор сотрет все типы и не будет иметь никакой информации о типах во время выполнения, если вы не используете отражение.Это означает: case Some(map: List[Map[String, Any]]) => println(map) по существу case Some(map: List[_]) => println(map) и, следовательно, совпадение будет успешным для любого параметра типа List, например List[Map[String, Any]], List[Map[String, Int]], List[String], List[Int] и т. Д.

Поэтому, Если вам нужно сопоставить такой универсальный контейнер, вы должны явно разрешить каждый контейнер и его вложенные подтипы.

def resolve(result: Any): Unit = result match {
    case Some(res) if res.isInstanceOf[List[_]] && res.asInstanceOf[List[_]].isEmpty  => println("Empty List.")  //Some(List())
    case Some(res) if res.isInstanceOf[List[_]] && !res.asInstanceOf[List[_]].exists(p => p.isInstanceOf[Map[_, _]] && p.asInstanceOf[Map[_, _]].nonEmpty) => println("List is not empty but each item of List is empty Map.")  //Some(List(Map(), Map()))
    case Some(res) if res.isInstanceOf[List[_]] && res.asInstanceOf[List[_]].filter(p => p.isInstanceOf[Map[_, _]] && p.asInstanceOf[Map[_, _]].nonEmpty).map(_.asInstanceOf[Map[_,_]]).exists(p => {p.head match {case e if e._1.isInstanceOf[String] && e._2.isInstanceOf[Any] => true; case _ => false}})  => println("Correct data.") // Some(List(Map("key1"-> 1), Map("key2" -> 2)))
    case None => println("none")
    case other => println("other")
}

val a: Option[Any] = Some(List())
val b: Option[Any] = Some(List(Map(), Map()))
val c: Option[Any] = Some(List(Map("key1"-> 1), Map("key2" -> 2)))
val d: Option[Any] = None
val e: Option[Any] = Some("apple")

resolve(a) // match first case
resolve(b) // match second case
resolve(c) // match third case
resolve(d) // match fourth case
resolve(e) // match fifth case
0 голосов
/ 28 сентября 2018

Ваш вывод Option[List[Map[String, Any]]], а не Option[Map[String, Any]].совпадение в списке, и вы будете в порядке:

import scala.util.parsing.json._
val result = JSON.parseFull("[{\"start\":\"starting\",\"test\":123,\"test2\":324,\"end\":\"ending\"}]")

val l: List[Map[String, Any]] = result match {
  case Some(list: List[Map[String, Any]]) => list
  case _ => throw new Exception("I shouldn't be here") // whatever for a non-match
}

Тогда вы можете map (если вы хотите, чтобы не возвращаемый тип Unit) / foreach (если вас не волнуетТип возврата единицы) в этом списке и делайте все, что вы хотите:

l.foreach(println)
l.map(_.toString) // or whatever you want ot do with the Map
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...