如何使用标准的Scala类来parsingScala中的JSON?
我在Scala 2.8中使用JSON类中的构build来parsingJSON代码。 我不想使用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] }
这是一个基于提取器的解决scheme,它将进行类的演员:
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)))
在for循环开始时,我将结果人为地包装在一个列表中,以便在最后得到一个列表。 然后在for循环的其余部分,我使用这样一个事实,即生成器(使用<-
)和值定义(使用=
)将使用unapply方法。
(较旧的答案编辑了 – 如果你好奇,检查编辑历史)
这是我做模式匹配的方式:
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) }
我喜欢@ huynhjl的回答,这使我走上了正确的道路。 但是,处理错误情况并不是很好。 如果所需的节点不存在,则会出现强制exception。 我已经稍微调整了这一点,以利用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) }
当然,这样做并不能处理错误,避免错误。 如果任何json节点丢失,这将产生一个空列表。 您可以使用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" } }
我尝试了一些东西,赞成模式匹配作为一种避免投射的方式,但在types擦除上遇到了麻烦。
主要的问题似乎是分析结果的完整types反映了JSON数据的结构,要么是繁琐的要么是不可能的。 我想这就是为什么任何用于截断types定义。 使用任何导致需要铸造。
我已经在下面简化了一些东西,但是对于问题中代码隐含的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}
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)) )