如何使用标准Scala类在Scala中解析JSON?

Phi*_*hil 109 json scala

我在Scala 2.8中使用JSON类中的构建来解析JSON代码.由于最小化依赖性,我不想使用Liftweb或其他任何一个.

我这样做的方式似乎太迫切了,有没有更好的方法呢?

import scala.util.parsing.json._
...
val json:Option[Any] = JSON.parseFull(jsonString)
val map:Map[String,Any] = json.get.asInstanceOf[Map[String, Any]]
val languages:List[Any] = map.get("languages").get.asInstanceOf[List[Any]]
languages.foreach( langMap => {
val language:Map[String,Any] = langMap.asInstanceOf[Map[String,Any]]
val name:String = language.get("name").get.asInstanceOf[String]
val isActive:Boolean = language.get("is_active").get.asInstanceOf[Boolean]
val completeness:Double = language.get("completeness").get.asInstanceOf[Double]
}
Run Code Online (Sandbox Code Playgroud)

huy*_*hjl 124

这是一个基于提取器的解决方案,它将进行类转换:

class CC[T] { def unapply(a:Any):Option[T] = Some(a.asInstanceOf[T]) }

object M extends CC[Map[String, Any]]
object L extends CC[List[Any]]
object S extends CC[String]
object D extends CC[Double]
object B extends CC[Boolean]

val jsonString =
    """
      {
        "languages": [{
            "name": "English",
            "is_active": true,
            "completeness": 2.5
        }, {
            "name": "Latin",
            "is_active": false,
            "completeness": 0.9
        }]
      }
    """.stripMargin

val result = for {
    Some(M(map)) <- List(JSON.parseFull(jsonString))
    L(languages) = map("languages")
    M(language) <- languages
    S(name) = language("name")
    B(active) = language("is_active")
    D(completeness) = language("completeness")
} yield {
    (name, active, completeness)
}

assert( result == List(("English",true,2.5), ("Latin",false,0.9)))
Run Code Online (Sandbox Code Playgroud)

在for循环的开始,我人为地将结果包装在一个列表中,以便在结尾处生成一个列表.然后在for循环的其余部分中,我使用了生成器(使用<-)和值定义(使用=)将使用unapply方法的事实.

(较旧的答案已编辑 - 如果您好奇,请查看编辑历史记录)


Mat*_*aun 18

这是我进行模式匹配的方式:

val result = JSON.parseFull(jsonStr)
result match {
  // Matches if jsonStr is valid JSON and represents a Map of Strings to Any
  case Some(map: Map[String, Any]) => println(map)
  case None => println("Parsing failed")
  case other => println("Unknown data structure: " + other)
}
Run Code Online (Sandbox Code Playgroud)


mur*_*yju 12

我喜欢@ huynhjl的答案,它让我走上了正确的道路.但是,它在处理错误条件方面并不是很好.如果所需节点不存在,则会出现强制转换异常.我已经稍微调整了这个Option以便更好地处理这个问题.

class CC[T] {
  def unapply(a:Option[Any]):Option[T] = if (a.isEmpty) {
    None
  } else {
    Some(a.get.asInstanceOf[T])
  }
}

object M extends CC[Map[String, Any]]
object L extends CC[List[Any]]
object S extends CC[String]
object D extends CC[Double]
object B extends CC[Boolean]

for {
  M(map) <- List(JSON.parseFull(jsonString))
  L(languages) = map.get("languages")
  language <- languages
  M(lang) = Some(language)
  S(name) = lang.get("name")
  B(active) = lang.get("is_active")
  D(completeness) = lang.get("completeness")
} yield {
  (name, active, completeness)
}
Run Code Online (Sandbox Code Playgroud)

当然,这并不能避免错误.如果缺少任何json节点,这将产生一个空列表.在执行之前,您可以使用a match检查节点是否存在...

for {
  M(map) <- Some(JSON.parseFull(jsonString))
} yield {
  map.get("languages") match {
    case L(languages) => {
      for {
        language <- languages
        M(lang) = Some(language)
        S(name) = lang.get("name")
        B(active) = lang.get("is_active")
        D(completeness) = lang.get("completeness")
      } yield {
        (name, active, completeness)
      }        
    }
    case None => "bad json"
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为CC unapply可以大大简化为`def unapply(a:Option [Any]):Option [T] = a.map(_.asInstanceOf [T])`. (2认同)

Don*_*zie 7

我尝试了一些方法,支持模式匹配作为一种避免转换的方法,但在集合类型上遇到类型擦除问题.

主要问题似乎是解析结果的完整类型反映了JSON数据的结构,并且要么很麻烦,要么无法完全陈述.我想这就是为什么任何用于截断类型定义.使用Any导致需要铸造.

我已经攻击了下面的内容,这些内容很简洁,但对于问题中的代码隐含的JSON数据非常具体.更通用的东西会更令人满意,但我不确定它是否会非常优雅.

implicit def any2string(a: Any)  = a.toString
implicit def any2boolean(a: Any) = a.asInstanceOf[Boolean]
implicit def any2double(a: Any)  = a.asInstanceOf[Double]

case class Language(name: String, isActive: Boolean, completeness: Double)

val languages = JSON.parseFull(jstr) match {
  case Some(x) => {
    val m = x.asInstanceOf[Map[String, List[Map[String, Any]]]]

    m("languages") map {l => Language(l("name"), l("isActive"), l("completeness"))}
  }
  case None => Nil
}

languages foreach {println}
Run Code Online (Sandbox Code Playgroud)


Yur*_*kha 5

val jsonString =
  """
    |{
    | "languages": [{
    |     "name": "English",
    |     "is_active": true,
    |     "completeness": 2.5
    | }, {
    |     "name": "Latin",
    |     "is_active": false,
    |     "completeness": 0.9
    | }]
    |}
  """.stripMargin

val result = JSON.parseFull(jsonString).map {
  case json: Map[String, List[Map[String, Any]]] =>
    json("languages").map(l => (l("name"), l("is_active"), l("completeness")))
}.get

println(result)

assert( result == List(("English", true, 2.5), ("Latin", false, 0.9)) )
Run Code Online (Sandbox Code Playgroud)

  • 这在最新的 scala Unbundled 中已被弃用。那么知道如何使用它吗? (3认同)