Skip to content

spray-json 源码剖析 #7

@debugger87

Description

@debugger87

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

很显然,我们还没有对 objectarray 这两种复杂 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 来实现。一张截图展示整个项目的代码组织结构:

screen shot 2015-07-07 at 11 39 06 am

鉴于篇幅有限,本文就主要介绍 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 的方法列表:

screen shot 2015-07-07 at 11 59 38 am

当然,最核心的方法当属 parseJsValuevalue

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]

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions