-
Notifications
You must be signed in to change notification settings - Fork 1
Description
spray-json 是一个用 Scala 实现的轻量 JSON library,其核心的 JsonParser 仅有三百多行代码,整个项目代码十分简洁,因此写篇博客简单介绍一下。
JSON 语法介绍
要想实现针对 JSON 的序列化与反序列化,首先需要对 JSON 的语法(grammar)有个比较清晰的认识。 RFC 4627 是这样定义 JSON 的:
A JSON text is a sequence of tokens. The set of tokens includes six
structural characters, strings, numbers, and three literal names.
A JSON text is a serialized object or array.
JSON-text = object / array
把 JSON text 里面主要的几种 Token 罗列一下:
-
structural characters共有六个:begin-array = ws %x5B ws ; [ left square bracket
begin-object = ws %x7B ws ; { left curly bracket
end-array = ws %x5D ws ; ] right square bracket
end-object = ws %x7D ws ; } right curly bracket
name-separator = ws %x3A ws ; : colon
value-separator = ws %x2C ws ; , comma -
在这六个
structural characters前后,允许出现空白(whitespace)字符ws:ws = *(
%x20 / ; Space
%x09 / ; Horizontal tab
%x0A / ; Line feed or New line
%x0D ; Carriage return
) -
三个
literal names:false = %x66.61.6c.73.65 ; false
null = %x6e.75.6c.6c ; null
true = %x74.72.75.65 ; true -
numbers:number = [ minus ] int [ frac ] [ exp ]
decimal-point = %x2E ; .
digit1-9 = %x31-39 ; 1-9
e = %x65 / %x45 ; e E
exp = e [ minus / plus ] 1_DIGIT
frac = decimal-point 1_DIGIT
int = zero / ( digit1-9 *DIGIT )
minus = %x2D ; -
plus = %x2B ; +
zero = %x30 ; 0 -
strings:string = quotation-mark *char quotation-mark
char = unescaped /
escape (
%x22 / ; " quotation mark U+0022
%x5C / ; \ reverse solidus U+005C
%x2F / ; / solidus U+002F
%x62 / ; b backspace U+0008
%x66 / ; f form feed U+000C
%x6E / ; n line feed U+000A
%x72 / ; r carriage return U+000D
%x74 / ; t tab U+0009
%x75 4HEXDIG ) ; uXXXX U+XXXX
escape = %x5C ;
quotation-mark = %x22 ; "
unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
有了上面这几种定义良好的 token,我们就可以定义 JSON value。
value = false / null / true / object / array / number / string
很显然,我们还没有对 object 和 array 这两种复杂 value 给出定义。
-
objects:object = begin-object [ member *( value-separator member ) ] end-object
member = string name-separator value -
arrays:array = begin-array [ value *( value-separator value ) ] end-array
spray-json 项目结构
spray-json 主要做的事情还是完成 JSON 的序列化(serialization)和反序列化(deserialization)。这两项任务主要由 JsonParser 和 JsonPrinter 来实现。一张截图展示整个项目的代码组织结构:
鉴于篇幅有限,本文就主要介绍 JsonParser 的实现。源码的注释是这样定义 JsonParser 的:
Fast, no-dependency parser for JSON as defined by http://tools.ietf.org/html/rfc4627.
整个 JsonParser 除了使用 Scala 原生的标准库或语言特性外,没有依赖任何第三方库。按照 JSON grammar 针对 JSON text 的定义,JsonParser 核心方法都对应 JSON text 的 token 以及 value。
- token 定义:
-
三个 literal names:
true,false,null -
structural characters:"[", "]", "{", "}", ",", ":"
-
numbers:
number -
strings:
string -
ws:
ws()
-
- value 定义:
-
object:
object -
array:
object -
value:
value
-
截图看看 JsonParser 的方法列表:
当然,最核心的方法当属 parseJsValue 和 value
def parseJsValue(): JsValue = {
ws()
`value`()
require(EOI)
jsValue
}private def `value`(): Unit = {
val mark = input.cursor
def simpleValue(matched: Boolean, value: JsValue) = if (matched) jsValue = value else fail("JSON Value", mark)
(cursorChar: @switch) match {
case 'f' => simpleValue(`false`(), JsFalse)
case 'n' => simpleValue(`null`(), JsNull)
case 't' => simpleValue(`true`(), JsTrue)
case '{' => advance(); `object`()
case '[' => advance(); `array`()
case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '-' => `number`()
case '"' => `string`(); jsValue = if (sb.length == 0) JsString.empty else JsString(sb.toString)
case _ => fail("JSON Value")
}
}反射?
与 lift-json 或 json4s 相比,spray-json 并没有提供运行时反射的功能。如果用户想要在运行时将某个 JSON 字符串反序列化之后实例化为某个 Scala 对象,spray-json 并不能帮你自动完成。不过,spray-json 还是提供了 JsonFormat 这样一个 trait,用户可以通过实现其中的 read 和 write 方法来完成类似反射的工作。
trait JsonReader[T] {
def read(json: JsValue): T
}
trait JsonWriter[T] {
def write(obj: T): JsValue
}
trait JsonFormat[T] extends JsonReader[T] with JsonWriter[T]
