用Scala徒手实现 JSON Parser
Posted 擎创夏洛克AIOps
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用Scala徒手实现 JSON Parser相关的知识,希望对你有一定的参考价值。
JSON(javascript Object Notation) 是一种轻量级的数据交换格式, 构建于两种结构:
“名称/值”对的集合
{ key-name : key-value }
key-name: 类型只能是string
key-value: 类型可能是number, string, boo, null, object, array
值的有序列表, 在大多数情况下可以理解为数组(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的主要内容,如果未能解决你的问题,请参考以下文章
为庆祝35岁生日,秃顶的我徒手用 Go 写个 Redis 服务器
如何测试 Json.parser 不是用 mockito java 调用的?
编译原理构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 7.)(笔记)解释器 interpreter 解析器 parser 抽象语法树AST(代码片