scala 中json 应用

Beautiful JSON parsing in Scala

You probably all know JSON - it's becoming the universal data exchange format, slowly but steadily replacing XML. In JavaScript, JSON is a proper first class citizen:

person = {
  "name": "Joe Doe",
  "age": 45,
  "kids": ["Frank", "Marta", "Joan"]
};

person.age;        // 45
person.kids[1];    // "Marta"

Sadly, it's not as easy in other languages. Scala does have a JSON parser in the standard library (scala.util.parsing.json.JSONcache), but it is terrible slow and the output is still not very nice.

val person_json = """{
  "name": "Joe Doe",
  "age": 45,
  "kids": ["Frank", "Marta", "Joan"]
}"""

val person = scala.util.parsing.json.JSON.parseFull(person_json)

// returns "Joe Doe"
person match {
  case Some(m: Map[String, Any]) => m("name") match {
    case s: String => s
  }
}

Luckily, it's easy to create a better and faster parser. I used the json-smartcache library to do the actual parsing (it's really fast!) and wrote a wrapper in Scala to make the results nicer to use.

A very important ingredient here is scala.Dynamiccache which allows us to handle arbitrary method calls. That means we will be able to use JSON like this:

person.name.toString      // "Joe Doe"
person.kids(1).toString   // "Marta"

Strictly speaking, the toString is not even necessary, as it's easy to define implicit conversions, but it makes it clearer what kind of result we expect.

Everything is built on two dynamic methods:

def selectDynamic(name: String): ScalaJSON = apply(name)    // used for object.field
def applyDynamic(name: String)(arg: Any) = {                // used for object.method(parm)
  arg match {
    case s: String => apply(name)(s)
    case n: Int => apply(name)(n)
    case u: Unit => apply(name)           // sometimes called when field is accessed
  }
}

The rest is really just a wrapper for the data structures returned by json-smart, but it allows you to write really concise code:

package EasyJSON

import JSON._

object Test extends App {
  print("Fetching recent data ...")
  val json = io.Source.fromURL("http://liquid8002.untergrund.net/infinigag/").mkString
  println(" done\n")

  val tree = parseJSON(json)

  println("next page: %d\n".format(tree.attributes.next.toInt))
  println("=== Top %d images from 9gag ===".format(tree.images.length))

  tree.images.foreach {
    case image =>
      println("%s: %s".format(image.title, image.image.thumb))
  }
}

Check out the full implementation here (it also allows you to generate JSON):

package EasyJSON

import net.minidev.json.JSONValue
import net.minidev.json.JSONArray
import net.minidev.json.JSONObject

import scala.collection.JavaConversions._

object JSON {
  def parseJSON(s: String) = new ScalaJSON(JSONValue.parse(s))
  def makeJSON(a: Any): String = a match {
    case m: Map[String, Any] => m.map {
      case (name, content) => "\"" + name + "\":" + makeJSON(content)
    }.mkString("{", ",", "}")
    case l: List[Any] => l.map(makeJSON).mkString("[", ",", "]")
    case l: java.util.List[Any] => l.map(makeJSON).mkString("[", ",", "]")
    case s: String => "\"" + s + "\""
    case i: Int => i.toString
  }

  implicit def ScalaJSONToString(s: ScalaJSON) = s.toString
  implicit def ScalaJSONToInt(s: ScalaJSON) = s.toInt
  implicit def ScalaJSONToDouble(s: ScalaJSON) = s.toDouble
}

case class JSONException extends Exception

class ScalaJSONIterator(i: java.util.Iterator[java.lang.Object]) extends Iterator[ScalaJSON] {
  def hasNext = i.hasNext()
  def next() = new ScalaJSON(i.next())
}

class ScalaJSON(o: java.lang.Object) extends Seq[ScalaJSON] with Dynamic {
  override def toString: String = o.toString
  def toInt: Int = o match {
    case i: Integer => i
    case _ => throw new JSONException
  }
  def toDouble: Double = o match {
    case d: java.lang.Double => d
    case f: java.lang.Float => f.toDouble
    case _ => throw new JSONException
  }
  def apply(key: String): ScalaJSON = o match {
    case m: JSONObject => new ScalaJSON(m.get(key))
    case _ => throw new JSONException
  }

  def apply(idx: Int): ScalaJSON = o match {
    case a: JSONArray => new ScalaJSON(a.get(idx))
    case _ => throw new JSONException
  }
  def length: Int = o match {
    case a: JSONArray => a.size()
    case m: JSONObject => m.size()
    case _ => throw new JSONException
  }
  def iterator: Iterator[ScalaJSON] = o match {
    case a: JSONArray => new ScalaJSONIterator(a.iterator())
    case _ => throw new JSONException
  }

  def selectDynamic(name: String): ScalaJSON = apply(name)
  def applyDynamic(name: String)(arg: Any) = {
    arg match {
      case s: String => apply(name)(s)
      case n: Int => apply(name)(n)
      case u: Unit => apply(name)
    }
  }
}

Category: misc

Tags: programming, web

← Back to Index

© Julian Schrittwieser. Built using Pelican. Theme by Giulio Fidente on github. .

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值