Я использую сборку в классе JSON в Scala 2.8 для разбора кода 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]
}
Это решение, основанное на экстракторах, которые будут выполнять класс:
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 ответ, это привело меня к правильному пути. Однако при обработке условий ошибок это не очень удобно. Если желаемый node не существует, вы получаете исключение литья. Я немного адаптировал это, чтобы использовать 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
для проверки наличия node перед тем, как действовать...
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"
}
}
def unapply(a: Option[Any]): Option[T] = a.map(_.asInstanceOf[T])
.
Я пробовал несколько вещей, предпочитая сопоставление шаблонов как способ избежать кастингов, но столкнулся с проблемами стирания типов в типах коллекций.
Основная проблема заключается в том, что полный тип результата анализа отражает структуру данных JSON и является либо громоздким, либо невозможным для полного состояния. Я думаю, именно поэтому Любой используется для усечения определений типов. Использование Любые приводит к необходимости кастинга.
Я взломал что-то ниже, которое является кратким, но чрезвычайно специфично для данных 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)) )
Вы можете сделать это так! Очень легко разобрать код JSON: P
package org.sqkb.service.common.bean
import java.text.SimpleDateFormat
import org.json4s
import org.json4s.JValue
import org.json4s.jackson.JsonMethods._
//import org.sqkb.service.common.kit.{IsvCode}
import scala.util.Try
/**
*
*/
case class Order(log: String) {
implicit lazy val formats = org.json4s.DefaultFormats
lazy val json: json4s.JValue = parse(log)
lazy val create_time: String = (json \ "create_time").extractOrElse("1970-01-01 00:00:00")
lazy val site_id: String = (json \ "site_id").extractOrElse("")
lazy val alipay_total_price: Double = (json \ "alipay_total_price").extractOpt[String].filter(_.nonEmpty).getOrElse("0").toDouble
lazy val gmv: Double = alipay_total_price
lazy val pub_share_pre_fee: Double = (json \ "pub_share_pre_fee").extractOpt[String].filter(_.nonEmpty).getOrElse("0").toDouble
lazy val profit: Double = pub_share_pre_fee
lazy val trade_id: String = (json \ "trade_id").extractOrElse("")
lazy val unid: Long = Try((json \ "unid").extractOpt[String].filter(_.nonEmpty).get.toLong).getOrElse(0L)
lazy val cate_id1: Int = (json \ "cate_id").extractOrElse(0)
lazy val cate_id2: Int = (json \ "subcate_id").extractOrElse(0)
lazy val cate_id3: Int = (json \ "cate_id3").extractOrElse(0)
lazy val cate_id4: Int = (json \ "cate_id4").extractOrElse(0)
lazy val coupon_id: Long = (json \ "coupon_id").extractOrElse(0)
lazy val platform: Option[String] = Order.siteMap.get(site_id)
def time_fmt(fmt: String = "yyyy-MM-dd HH:mm:ss"): String = {
val dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val date = dateFormat.parse(this.create_time)
new SimpleDateFormat(fmt).format(date)
}
}
JSON.parseFull
возвращаетOption[Any]
, поэтому он начинается сList(None)
илиList(Some(any))
.Some
для сопоставления с образцом вOption
.