用Scala徒手实现 JSON Parser

Posted 擎创夏洛克AIOps

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用Scala徒手实现 JSON Parser相关的知识,希望对你有一定的参考价值。

ITOA | 技术分享     ↑ 点击“夏洛克ITOA”关注我们 



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")...

当需要取叶子结点的值时

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)    })  } }


来看看测试吧


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的读取.






夏洛克 ITOA

Make Data Think

人工智能 | 机器学习 | IT运维




以上是关于用Scala徒手实现 JSON Parser的主要内容,如果未能解决你的问题,请参考以下文章

从零开始用函数式实现 JSON Parser

为庆祝35岁生日,秃顶的我徒手用 Go 写个 Redis 服务器

如何测试 Json.parser 不是用 mockito java 调用的?

编译原理构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 7.)(笔记)解释器 interpreter 解析器 parser 抽象语法树AST(代码片

Node.JS从相识到相知--徒手搭建简易版json服务

为啥 json-parser 解析错误我的 json