Scala徒手实现JSON Parser

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式, 构建于两种结构:

1. “名称/值”对的集合

{
    key-name : key-value
}
- key-name: 类型只能是string
- key-value: 类型可能是number, string, boo, null, object, array

2. 值的有序列表, 在大多数情况下可以理解为数组(Array)

[object, object, ...]

用scala实现上面JSON的定义

class JsonParser extends JavaTokenParsers {

  def jNum: Parser[Double] = floatingPointNumber ^^ (_.toDouble)

  def jStr: Parser[String] = stringLiteral ^^ (s => s.substring(1, s.length() - 1))

  def jBool: Parser[Boolean] = "(true|false)".r ^^ (_.toBoolean)

  def jNull: Parser[Null] = "null".r ^^ (t => null)

  def term = jsonArray | jsonObject | jNum | jBool | jNull | jStr

  def jsonArray: Parser[List[Any]] = "[" ~> rep(term <~ ",?".r) <~ "]" ^^ (l => l)

  def jsonObject: Parser[Map[String,Any]] = "{" ~> rep(
    (
      jStr ~ ":" ~ jNum |
        jStr ~ ":" ~ jBool |
        jStr ~ ":" ~ jNull |
        jStr ~ ":" ~ jsonObject |
        jStr ~ ":" ~ jsonArray |
        jStr ~ ":" ~ jStr
      ) <~ ",?".r
  ) <~ "}" ^^ {
    os =>
      var map = Map[String,Any]()
      os.foreach(o =>
        o match {
          case k ~ ":" ~ v => map = map ++ Map(k->v)
        })
      map
  }
}

这个 parser看上去还是相当简单的, 我们来测试下功能

val json =
      """{
        | "key":"yardville",
        | "doc_count":2,
        | "avg_age":{
        |   "value":37.0
        |   },
        | "count":{
        |   "value":2
        |   }
        |}
      """.stripMargin

    val jsonObject = JsonParser.parse(JsonParser.jsonObject,json)
    val result = jsonObject.get("key")
    println(result)

执行结果如下:

result is: yardville

继续改进

从上面看来, 如果我对JSON对象访问路径是:count/value, 或者层次比较深, 如:A/B/C/D/E, 操作起来不太方便, 如果能够像下面这样操作, 生活是不是太美好了!!! 比如:

jsonObject.get("count").get("value").asInt

让我们来先创建JsonObject类

class JsonObject(value:Any) {

  def isJsonObject = {
    this.value.isInstanceOf[Map[String,Any]]
  }
  def asJsonObject = {
    if(isJsonObject) {
      this.value.asInstanceOf[Map[String,Any]]
    } else {
      throw new ClassCastException(s"it is not json object")
    }
  }
  def getJsonObject(key:String) = {
    new JsonObject(this.asJsonObject.get(key).get)
  }

  def get(key:String) = {
    new JsonElement(this.asJsonObject.get(key).get)
  }
}

这时候我们完成了无限级的get

jsonObject.getJsonObject("A").getJsonObject("B")...

当需要取叶子结点的值时

扫描二维码关注公众号,回复: 888439 查看本文章

jsonObject.getJsonObject("A").getJsonObject("B").get("C").asString

我们来看看JsonElement的实现

class JsonElement(value:Any) {

  def isObject = {
    this.value.isInstanceOf[Object]
  }
  def asObject = {
    if(isObject) {
      this.value.asInstanceOf[Object]
    } else {
      null
    }
  }

  def asString = {
    if(asObject!=null)
      asObject.toString
    else {
      ""
    }
  }

 def isDoubel = {
    this.value.isInstanceOf[Double]
  }
  def asDouble = {
    if(isDoubel) {
      this.value.asInstanceOf[Double]
    } else {
      0d
    }
  }

  def isBoolean = {
    this.value.isInstanceOf[Boolean]
  }
  def asBoolean = {
    if(isBoolean) {
      this.value.asInstanceOf[Boolean]
    } else {
      false
    }
  }
}

我们还需要一个入口类 JsonMapper

class JsonMapper (text:String) {

  def getJsonObject(key:String) = {
    val result = JsonParser.parseAll(JsonParser.jsonObject,text)
    val eleMap = result.get

    new JsonObject(eleMap.get(key).get)
  }

  def getJsonArray(text:String) = {
    JsonParser.parseAll(JsonParser.jsonArray,text).get.map(l=>{
      new JsonObject(l)
    })
  }
}

来看看测试吧

  1. json-object-test
val dataMapping: String = """
          {  "temp": {
                "properties": {
                    "temp": {
                      "type": "string",
                      "store": true
                    }
                }
             }
          }"""
    val mapper = new JsonMapper(dataMapping)
    val jsonObject = mapper.getJsonObject("temp")
    val temp = jsonObject.getJsonObject("properties").getJsonObject("temp")
    val type_string = temp.get("type").asString
    val store = temp.get("store").asObject
    println(s"type:$type_string")
    println(s"store:$store")

结果如下:

type:string

store:true

  • json-array-test
val dataMappping =
      """[
        | {"key":"yardville","doc_count":2,"avg_age":{"value":37.0},"count":{"value":2}},
        | {"key":"camino","doc_count":1,"avg_age":{"value":37.0},"count":{"value":1}},
        | {"key":"delshire","doc_count":1,"avg_age":{"value":36.0},"count":{"value":1}},
        | {"key":"forestburg","doc_count":1,"avg_age":{"value":37.0},"count":{"value":1}},
        | {"key":"foscoe","doc_count":1,"avg_age":{"value":31.0},"count":{"value":1}}
        |]
      """.stripMargin

    val mapper = new JsonMapper(dataMappping)
    val array = mapper.getJsonArray(dataMappping)
    val first = array(0)
    val key = first.get("key").asString
    val age_value = first.getJsonObject("avg_age").get("value").asObject
    val count_value = first.getJsonObject("count").get("value").asDouble.toInt
    println(s"key: $key")
    println(s"age avg: $age_value")
    println(s"count: $count_value")

结果如下:

key: yardville

age avg: 37.0

count: 2

到些为止, 我们没有依赖任何JSON序列化工具, 完成的JSON string的读取.

猜你喜欢

转载自my.oschina.net/u/2392330/blog/1358721
今日推荐