leptjson

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 16, 2025 License: MIT Imports: 1 Imported by: 0

README

从零开始的 JSON 库教程(一):启程 (Go语言版)

1. JSON 是什么

JSON(JavaScript Object Notation)是一个用于数据交换的文本格式,现时的标准为ECMA-404

虽然 JSON 源至于 JavaScript 语言,但它只是一种数据格式,可用于任何编程语言。现时具类似功能的格式有 XML、YAML,当中以 JSON 的语法最为简单。

JSON 是树状结构,而 JSON 只包含 6 种数据类型:

  • null: 表示为 null
  • boolean: 表示为 true 或 false
  • number: 一般的浮点数表示方式
  • string: 表示为 "..."
  • array: 表示为 [ ... ]
  • object: 表示为 { ... }

我们要实现的 JSON 库,主要是完成 3 个需求:

  1. 把 JSON 文本解析为一个树状数据结构(parse)。
  2. 提供接口访问该数据结构(access)。
  3. 把数据结构转换成 JSON 文本(stringify)。

在本单元中,我们只实现最简单的 null 值解析。

2. Go语言项目结构

与C语言不同,Go语言有自己的项目组织方式和包管理系统。我们的JSON库将使用Go模块来组织代码。

我们的 JSON 库名为 leptjson,代码文件有:

  1. leptjson.go:leptjson 的实现文件,包含类型定义和函数实现。
  2. leptjson_test.go:测试文件,包含单元测试。

3. API 设计

Go语言有自己的类型系统和错误处理机制,我们的API设计会利用这些特性。

首先,我们定义JSON值的类型:

type ValueType int

const (
	NULL ValueType = iota
	FALSE
	TRUE
	NUMBER
	STRING
	ARRAY
	OBJECT
)

然后,我们定义解析过程中可能出现的错误:

type ParseError int

const (
	PARSE_OK ParseError = iota
	PARSE_EXPECT_VALUE
	PARSE_INVALID_VALUE
	PARSE_ROOT_NOT_SINGULAR
)

接着,我们定义JSON值的数据结构:

type Value struct {
	Type ValueType
}

最后,我们提供两个主要的API函数:

func Parse(v *Value, json string) ParseError
func GetType(v *Value) ValueType

4. JSON 语法子集

下面是此单元的 JSON 语法子集,使用 ABNF 表示:

JSON-text = ws value ws
ws = *(%x20 / %x09 / %x0A / %x0D)
value = null 
null  = "null"

在这个语法子集下,我们定义了3种错误码:

  • 若一个 JSON 只含有空白,返回 PARSE_EXPECT_VALUE
  • 若一个值之后,在空白之后还有其他字符,返回 PARSE_ROOT_NOT_SINGULAR
  • 若值不是 null,返回 PARSE_INVALID_VALUE

5. 单元测试

Go语言内置了测试框架,我们使用它来编写单元测试。测试文件命名为 xxx_test.go,测试函数命名为 TestXxx

我们的测试代码包含以下测试函数:

func TestParseNull(t *testing.T)
func TestParseExpectValue(t *testing.T)
func TestParseInvalidValue(t *testing.T)
func TestParseRootNotSingular(t *testing.T)

6. 实现解析器

我们的解析器实现了以下几个函数:

func parseWhitespace(c *context)
func parseNull(c *context, v *Value) ParseError
func parseValue(c *context, v *Value) ParseError
func Parse(v *Value, json string) ParseError

解析过程使用递归下降解析器(recursive descent parser)的方法。从最顶层的 Parse 函数开始,它会跳过空白字符,然后调用 parseValue 函数解析JSON值。

parseValue 函数会根据当前字符决定调用哪个具体的解析函数,例如 parseNull

7. 运行测试

在Go语言中,测试是通过内置的go test命令来运行的。要运行我们的JSON库测试,可以使用以下命令:

# 在tutorial01目录下运行所有测试
go test

# 运行测试并显示详细输出
go test -v

# 运行特定的测试函数
go test -run TestParseNull
运行基准测试

基准测试用于测量代码的性能。要运行基准测试,可以使用以下命令:

# 运行所有基准测试
go test -bench=.

# 运行特定的基准测试
go test -bench=BenchmarkParseNull

# 运行基准测试并显示内存分配信息
go test -bench=. -benchmem

基准测试的输出示例:

BenchmarkParseNull-8       10000000               118 ns/op              0 B/op          0 allocs/op
BenchmarkParseTrue-8       10000000               119 ns/op              0 B/op          0 allocs/op
BenchmarkParseFalse-8      10000000               120 ns/op              0 B/op          0 allocs/op

输出结果解释:

  • 第一列:基准测试名称和CPU核心数
  • 第二列:测试运行的次数
  • 第三列:每次操作的平均时间(纳秒)
  • 第四列:每次操作分配的内存(字节)
  • 第五列:每次操作的内存分配次数

8. 总结与练习

在本单元中,我们:

  1. 设计了JSON库的API
  2. 实现了解析null值的功能
  3. 编写了单元测试
  4. 学习了如何运行测试和基准测试

练习:

  1. 完善解析器,增加对true和false的解析
  2. 增加相应的单元测试

Documentation

Overview

Package leptjson 实现了一个轻量级的JSON解析器 支持解析以下JSON值类型: - null - true - false - number - string - array - object

使用示例:

v := Value{}
if err := Parse(&v, "null"); err != PARSE_OK {
    // 处理错误
}
if GetType(&v) == NULL {
    // 处理null值
}

特殊情况处理: 1. null值解析:

  • 必须完全匹配"null"字符串
  • 大小写敏感,如"NULL"、"Null"等都是非法的
  • 不能包含额外的空白字符

2. true值解析:

  • 必须完全匹配"true"字符串
  • 大小写敏感,如"TRUE"、"True"等都是非法的
  • 不能包含额外的空白字符

3. false值解析:

  • 必须完全匹配"false"字符串
  • 大小写敏感,如"FALSE"、"False"等都是非法的
  • 不能包含额外的空白字符

4. 空白字符处理:

  • 支持空格、制表符(\t)、换行符(\n)和回车符(\r)
  • 空白字符可以出现在JSON值的任意位置
  • 多个连续的空白字符是合法的

5. 错误处理:

  • PARSE_EXPECT_VALUE: 输入为空或只包含空白字符
  • PARSE_INVALID_VALUE: 输入格式不正确
  • PARSE_ROOT_NOT_SINGULAR: 输入包含多个值

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ParseError

type ParseError int

ParseError 表示解析过程中可能出现的错误

const (
	PARSE_OK                ParseError = iota // 解析成功
	PARSE_EXPECT_VALUE                        // 期望一个值
	PARSE_INVALID_VALUE                       // 无效的值
	PARSE_ROOT_NOT_SINGULAR                   // 根节点不唯一
)

解析错误常量

func Parse

func Parse(v *Value, json string) ParseError

Parse 解析JSON字符串 参数:

  • v: 用于存储解析结果的Value指针
  • json: 要解析的JSON字符串

返回值:

  • ParseError: 解析结果,PARSE_OK表示成功

注意:

  • 解析前会重置Value对象
  • 解析失败时会设置Value类型为NULL
Example (Error)

示例 - 错误处理

v := Value{}
err := Parse(&v, "invalid")
fmt.Println(err)
Output:
无效的值
Example (False)

示例 - 解析false值

v := Value{}
Parse(&v, "false")
fmt.Println(v.String())
Output:
false
Example (Null)

示例 - 解析null值

v := Value{}
Parse(&v, "null")
fmt.Println(v.String())
Output:
null
Example (True)

示例 - 解析true值

v := Value{}
Parse(&v, "true")
fmt.Println(v.String())
Output:
true

func (ParseError) Error

func (e ParseError) Error() string

Error 实现error接口,返回错误描述

type Value

type Value struct {
	Type  ValueType
	Num   float64
	Str   string
	Array []*Value
}

Value 表示一个JSON值 字段说明: - Type: 值的类型 - Num: 数值(当Type为NUMBER时使用) - Str: 字符串值(当Type为STRING时使用) - Array: 数组值(当Type为ARRAY时使用)

func (*Value) String

func (v *Value) String() string

String 实现fmt.Stringer接口,返回Value的字符串表示

type ValueType

type ValueType int

ValueType 表示JSON值的类型

const (
	NULL   ValueType = iota // null值
	FALSE                   // false值
	TRUE                    // true值
	NUMBER                  // 数字
	STRING                  // 字符串
	ARRAY                   // 数组
	OBJECT                  // 对象
)

JSON值类型常量

func GetType

func GetType(v *Value) ValueType

GetType 获取Value的类型 参数:

  • v: Value指针

返回值:

  • ValueType: 值的类型

func (ValueType) String

func (t ValueType) String() string

String 返回ValueType的字符串表示

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL