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: 6 Imported by: 0

README

从零开始的 JSON 库教程(十一):JSON Schema 验证

JSON Schema 简介

JSON Schema 是一种基于 JSON 格式定义的规范,用于验证、描述和标注 JSON 文档。它提供了一种描述 JSON 数据结构的标准方法,可以用来:

  • 验证数据符合预期的格式和内容
  • 提供清晰、人类可读的文档
  • 自动生成表单或配置界面
  • 支持数据自动完成功能
  • 验证服务器和客户端之间的数据交换

本章实现了 JSON Schema Draft 7 规范的核心部分,作为我们 JSON 库的扩展功能。

实现概述

我们的 JSON Schema 验证器支持以下功能:

  1. 基本类型验证:验证 JSON 数据的类型(字符串、数字、整数、布尔值、空值、对象和数组)
  2. 数值约束:支持最小值、最大值、独占范围和倍数验证
  3. 字符串约束:支持最小/最大长度、正则表达式模式和常见格式(如电子邮件和日期时间)
  4. 数组约束:支持最小/最大元素数量、元素唯一性和元素类型验证
  5. 对象约束:支持最小/最大属性数、必需属性、属性模式和附加属性控制
  6. 逻辑组合:支持 allOf(所有模式都匹配)、anyOf(至少一个模式匹配)、oneOf(恰好一个模式匹配)和 not(模式不匹配)

关键结构

SchemaValidationError

表示验证过程中发现的错误,包含错误路径和消息:

type SchemaValidationError struct {
    Path    string // 导致错误的 JSON 路径
    Message string // 错误描述
}
SchemaValidationResult

保存验证的最终结果,包括验证是否通过和错误列表:

type SchemaValidationResult struct {
    Valid  bool                    // 是否验证通过
    Errors []SchemaValidationError // 验证错误列表
}
JSONSchema

表示一个 JSON Schema 对象,提供验证方法:

type JSONSchema struct {
    Schema *Value // 存储 JSON Schema 的 Value 对象
}

使用示例

创建一个 JSON Schema 并验证数据:

package main

import (
    "fmt"
    "github.com/Cactusinhand/go-json-tutorial/tutorial11"
)

func main() {
    // 定义 Schema
    schemaJSON := `{
        "type": "object",
        "required": ["name", "age"],
        "properties": {
            "name": {
                "type": "string",
                "minLength": 2
            },
            "age": {
                "type": "integer",
                "minimum": 18
            },
            "email": {
                "type": "string",
                "format": "email"
            }
        }
    }`
    
    // 解析 Schema
    schema, err := leptjson.NewJSONSchema(schemaJSON)
    if err != nil {
        fmt.Printf("Schema 创建失败: %v\n", err)
        return
    }
    
    // 要验证的有效数据
    validJSON := `{
        "name": "张三",
        "age": 25,
        "email": "zhangsan@example.com"
    }`
    validValue := &leptjson.Value{}
    if err := leptjson.Parse(validValue, validJSON); err != leptjson.PARSE_OK {
        fmt.Printf("数据解析失败: %v\n", err)
        return
    }
    
    // 验证有效数据
    result := schema.Validate(validValue)
    if result.Valid {
        fmt.Println("数据验证通过!")
    } else {
        fmt.Println("数据验证失败:")
        for _, err := range result.Errors {
            fmt.Printf("- %s\n", err.Error())
        }
    }
    
    // 要验证的无效数据
    invalidJSON := `{
        "name": "李",
        "age": 16
    }`
    invalidValue := &leptjson.Value{}
    if err := leptjson.Parse(invalidValue, invalidJSON); err != leptjson.PARSE_OK {
        fmt.Printf("数据解析失败: %v\n", err)
        return
    }
    
    // 验证无效数据
    result = schema.Validate(invalidValue)
    if !result.Valid {
        fmt.Println("\n无效数据验证(预期失败):")
        for _, err := range result.Errors {
            fmt.Printf("- %s\n", err.Error())
        }
    }
}

输出结果:

数据验证通过!

无效数据验证(预期失败):
- 位于 'name': 字符串长度 1 小于最小长度 2
- 位于 'age': 值 16 小于最小值 18

支持的 JSON Schema 关键字

通用关键字
  • type: 指定值的类型,可以是单一类型或类型数组
  • enum: 限制值为指定的枚举列表中的一个
  • const: 要求值必须等于指定的常量
数值关键字
  • minimum: 指定最小值(包含)
  • exclusiveMinimum: 指定独占最小值(不包含)
  • maximum: 指定最大值(包含)
  • exclusiveMaximum: 指定独占最大值(不包含)
  • multipleOf: 要求值是指定数的倍数
字符串关键字
  • minLength: 指定最小字符串长度
  • maxLength: 指定最大字符串长度
  • pattern: 指定字符串必须匹配的正则表达式
  • format: 指定字符串格式(如电子邮件、日期时间等)
数组关键字
  • minItems: 指定最小数组长度
  • maxItems: 指定最大数组长度
  • uniqueItems: 要求数组元素必须唯一
  • items: 指定数组元素的模式(对象或数组)
  • contains: 要求数组至少包含一个匹配指定模式的元素
对象关键字
  • minProperties: 指定最小属性数量
  • maxProperties: 指定最大属性数量
  • required: 指定必需的属性名数组
  • properties: 定义对象的属性及其模式
  • patternProperties: 使用正则表达式匹配属性名并验证其值
  • additionalProperties: 控制未在 properties 中定义的属性
  • propertyNames: 验证所有属性名
  • dependencies: 指定当某个属性存在时,其他属性也必须存在
逻辑关键字
  • allOf: 要求值同时满足所有指定的模式
  • anyOf: 要求值至少满足一个指定的模式
  • oneOf: 要求值恰好满足一个指定的模式
  • not: 要求值不满足指定的模式

限制与未来改进

当前实现有以下限制:

  1. 不支持 Schema 引用($ref)和模式重用
  2. 不支持基于 URI 的模式标识符($id)
  3. 不支持默认值(default)关键字的处理
  4. 条件验证(if/then/else)尚未实现
  5. 某些格式验证的实现是简化的

未来改进方向:

  1. 支持 Schema 引用和 $ref 解析
  2. 实现条件验证(if/then/else)
  3. 支持注释关键字(title, description, examples等)
  4. 增加更多格式验证(date, time, hostname等)
  5. 支持自定义格式验证器
  6. 完善错误信息,提供更友好的错误提示

参考资料

Documentation

Overview

cycle_detection.go - 循环引用检测实现

json_pointer.go - JSON指针实现 (RFC6901)

json_schema.go - JSON Schema 验证实现(基于部分 JSON Schema Draft 7)

leptjson.go - Go语言版JSON库

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildJSONPointer

func BuildJSONPointer(segments ...interface{}) (string, error)

BuildJSONPointer 创建一个JSON指针字符串

func ClearArray

func ClearArray(v *Value)

ClearArray 清空数组的所有元素

func ClearObject

func ClearObject(v *Value)

ClearObject 清空对象的所有成员

func Copy

func Copy(dst, src *Value)

Copy 深度复制一个JSON值

func CopySafe

func CopySafe(dst, src *Value) error

CopySafe 安全复制JSON值,避免循环引用

func CopySafeWithReplacement

func CopySafeWithReplacement(dst, src *Value)

CopySafeWithReplacement 带替换的安全复制

func CustomCopySafeWithReplacement

func CustomCopySafeWithReplacement(dst, src *Value, replacer CircularReplacer)

CustomCopySafeWithReplacement 使用自定义替换器的安全复制

func Equal

func Equal(lhs, rhs *Value) bool

Equal 判断两个JSON值是否相等

func EraseArrayElement

func EraseArrayElement(v *Value, index, count int)

EraseArrayElement 删除数组中从index开始的count个元素

func FindObjectIndex

func FindObjectIndex(v *Value, key string) int

FindObjectIndex 查找JSON对象中指定键的索引

func Free

func Free(v *Value)

Free 释放JSON值占用的资源

func GetArrayCapacity

func GetArrayCapacity(v *Value) int

GetArrayCapacity 获取数组当前的容量

func GetArraySize

func GetArraySize(v *Value) int

GetArraySize 获取JSON数组的大小

func GetBoolean

func GetBoolean(v *Value) bool

GetBoolean 获取JSON布尔值

func GetErrorMessage

func GetErrorMessage(code ParseError) string

GetErrorMessage 根据错误码获取错误消息

func GetNumber

func GetNumber(v *Value) float64

GetNumber 获取JSON数字值

func GetObjectCapacity

func GetObjectCapacity(v *Value) int

GetObjectCapacity 获取对象的容量

func GetObjectKey

func GetObjectKey(v *Value, index int) string

GetObjectKey 获取JSON对象的键

func GetObjectSize

func GetObjectSize(v *Value) int

GetObjectSize 获取JSON对象的大小

func GetString

func GetString(v *Value) string

GetString 获取JSON字符串值

func HasCycle

func HasCycle(v *Value) bool

HasCycle 检测JSON值中是否存在循环引用

func Move

func Move(dst, src *Value)

Move 将源值移动到目标值,并将源值设为null

func ParseJSONPointer

func ParseJSONPointer(pointer string) (*JSONPointer, JSONPointerError)

ParseJSONPointer 解析JSON指针字符串 例如: "/foo/0/bar" => ["foo", "0", "bar"]

func PopBackArrayElement

func PopBackArrayElement(v *Value)

PopBackArrayElement 移除数组末尾的元素

func RemoveObjectValue

func RemoveObjectValue(v *Value, index int)

RemoveObjectValue 移除对象中指定索引的成员

func RemoveValueByPointer

func RemoveValueByPointer(v *Value, pointerStr string) error

RemoveValueByPointer 使用JSON指针删除值

func ReserveArray

func ReserveArray(v *Value, capacity int)

ReserveArray 扩充数组容量

func ReserveObject

func ReserveObject(v *Value, capacity int)

ReserveObject 扩充对象容量

func SafeCopyWithReplacer

func SafeCopyWithReplacer(dst, src *Value, replacer CircularReplacer)

SafeCopyWithReplacer 带替换器的安全复制,处理循环引用

func SetArray

func SetArray(v *Value, capacity int)

SetArray 设置值为数组类型,可以预分配容量

func SetBoolean

func SetBoolean(v *Value, b bool)

SetBoolean 设置JSON布尔值

func SetNull

func SetNull(v *Value)

SetNull 将值设置为NULL类型

func SetNumber

func SetNumber(v *Value, n float64)

SetNumber 设置JSON数字值

func SetObject

func SetObject(v *Value)

SetObject 设置值为对象类型,可以预分配容量

func SetString

func SetString(v *Value, s string)

SetString 设置JSON字符串值

func SetValueByPointer

func SetValueByPointer(v *Value, pointerStr string, value *Value) error

SetValueByPointer 使用JSON指针设置值

func ShrinkArray

func ShrinkArray(v *Value)

ShrinkArray 缩小数组容量至实际大小

func ShrinkObject

func ShrinkObject(v *Value)

ShrinkObject 缩小对象容量至实际大小

func Swap

func Swap(lhs, rhs *Value)

Swap 交换两个JSON值

Types

type CircularReplacer

type CircularReplacer func(path []string) *Value

CircularReplacer 定义了在发现循环引用时的替换函数类型

type CycleError

type CycleError int

CycleError 表示循环引用错误

const (
	CYCLE_OK CycleError = iota
	CYCLE_DETECTED
)

循环引用错误常量

func DetectCycle

func DetectCycle(v *Value) CycleError

DetectCycle 检测JSON值中是否存在循环引用

func SafeCopy

func SafeCopy(dst, src *Value) CycleError

SafeCopy 安全复制JSON值,检测并处理循环引用

func (CycleError) Error

func (e CycleError) Error() string

实现 Error 接口

type EnhancedError

type EnhancedError struct {
	Code          ParseError // 错误码
	Message       string     // 错误消息
	Line          int        // 行号
	Column        int        // 列号
	Context       string     // 错误发生的上下文
	Pointer       string     // 错误位置指针(比如 "----^")
	SourceInput   string     // 输入源
	IsRecoverable bool       // 是否可恢复
}

EnhancedError 定义了一个增强的错误类型,包含详细信息

func (*EnhancedError) Error

func (e *EnhancedError) Error() string

Error 实现error接口

type JSONPointer

type JSONPointer struct {
	// contains filtered or unexported fields
}

JSONPointer 表示一个JSON指针(RFC6901)

func GetJSONPointer

func GetJSONPointer(segments ...interface{}) (*JSONPointer, error)

GetJSONPointer 创建一个指向指定路径的JSONPointer 例如: NewJSONPointer("foo", 0, "bar") => "/foo/0/bar"

func (*JSONPointer) Get

func (p *JSONPointer) Get(root *Value) (*Value, JSONPointerError)

Get 根据JSON指针获取值

func (*JSONPointer) Remove

func (p *JSONPointer) Remove(root *Value) JSONPointerError

Remove 根据JSON指针删除值

func (*JSONPointer) Set

func (p *JSONPointer) Set(root *Value, value *Value) JSONPointerError

Set 根据JSON指针设置值

func (*JSONPointer) String

func (p *JSONPointer) String() string

创建一个JSON指针字符串表示

type JSONPointerError

type JSONPointerError int

JSONPointerError 表示JSON指针相关错误

const (
	POINTER_OK JSONPointerError = iota
	POINTER_INVALID_FORMAT
	POINTER_INDEX_OUT_OF_RANGE
	POINTER_KEY_NOT_FOUND
	POINTER_INVALID_TARGET
)

JSON指针错误常量

func (JSONPointerError) Error

func (e JSONPointerError) Error() string

实现 Error 接口

type JSONSchema

type JSONSchema struct {
	Schema *Value // 存储 JSON Schema 的 Value 对象
}

JSONSchema 表示一个 JSON Schema 对象

func NewJSONSchema

func NewJSONSchema(schemaJSON string) (*JSONSchema, error)

NewJSONSchema 创建一个新的 JSON Schema

func NewJSONSchemaFromValue

func NewJSONSchemaFromValue(schema *Value) (*JSONSchema, error)

NewJSONSchemaFromValue 从 Value 对象创建 JSON Schema

func (*JSONSchema) Validate

func (js *JSONSchema) Validate(data *Value) *SchemaValidationResult

Validate 根据 Schema 验证 JSON 数据

type Member

type Member struct {
	K string // 键
	V *Value // 值
}

Member 表示对象的成员(键值对)

type ParseError

type ParseError int

ParseError 表示解析错误

const (
	PARSE_OK                           ParseError = iota // 解析成功
	PARSE_EXPECT_VALUE                                   // 期望一个值
	PARSE_INVALID_VALUE                                  // 无效的值
	PARSE_ROOT_NOT_SINGULAR                              // 根节点不唯一
	PARSE_NUMBER_TOO_BIG                                 // 数字太大
	PARSE_MISS_QUOTATION_MARK                            // 缺少引号
	PARSE_INVALID_STRING_ESCAPE                          // 无效的转义序列
	PARSE_INVALID_STRING_CHAR                            // 无效的字符
	PARSE_INVALID_UNICODE_HEX                            // 无效的Unicode十六进制
	PARSE_INVALID_UNICODE_SURROGATE                      // 无效的Unicode代理对
	PARSE_MISS_COMMA_OR_SQUARE_BRACKET                   // 缺少逗号或方括号
	PARSE_MISS_KEY                                       // 缺少键
	PARSE_MISS_COLON                                     // 缺少冒号
	PARSE_MISS_COMMA_OR_CURLY_BRACKET                    // 缺少逗号或花括号
	PARSE_MAX_DEPTH_EXCEEDED                             // 超过最大嵌套深度
	PARSE_COMMENT_NOT_CLOSED                             // 注释未闭合
)

解析错误常量

func Parse

func Parse(v *Value, json string) ParseError

Parse 解析JSON文本(使用默认选项)

func ParseWithOptions

func ParseWithOptions(v *Value, json string, options ParseOptions) ParseError

ParseWithOptions 使用自定义选项解析JSON文本

解析步骤: 1. 跳过前导空白字符 2. 解析JSON值 3. 跳过后续空白字符 4. 检查是否还有额外内容(这将导致PARSE_ROOT_NOT_SINGULAR错误)

func (ParseError) Error

func (e ParseError) Error() string

Error 返回解析错误的描述

type ParseOptions

type ParseOptions struct {
	MaxDepth          int  // 最大嵌套深度
	AllowComments     bool // 是否允许注释
	AllowTrailing     bool // 是否允许尾随逗号
	StrictMode        bool // 严格模式(更严格的检查)
	RecoverFromErrors bool // 是否从非致命错误恢复
}

ParseOptions 定义解析选项

func DefaultParseOptions

func DefaultParseOptions() ParseOptions

DefaultParseOptions 返回默认解析选项

type SchemaValidationError

type SchemaValidationError struct {
	Path    string // 导致错误的 JSON 路径
	Message string // 错误描述
}

SchemaValidationError 表示 JSON Schema 验证错误

func (SchemaValidationError) Error

func (e SchemaValidationError) Error() string

实现 Error 接口

type SchemaValidationResult

type SchemaValidationResult struct {
	Valid  bool                    // 是否验证通过
	Errors []SchemaValidationError // 验证错误列表
}

SchemaValidationResult 存储验证结果

func (*SchemaValidationResult) AddError

func (r *SchemaValidationResult) AddError(path, message string)

AddError 添加验证错误

type StringifyError

type StringifyError int

StringifyError 表示字符串化错误

const (
	STRINGIFY_OK StringifyError = iota // 字符串化成功
)

字符串化错误常量

func Stringify

func Stringify(v *Value) (string, StringifyError)

Stringify 将Value转换为JSON字符串

func (StringifyError) Error

func (e StringifyError) Error() string

Error 返回字符串化错误的描述

type Value

type Value struct {
	Type ValueType `json:"type"` // 值类型
	N    float64   `json:"n"`    // 数字值(当Type为NUMBER时有效)
	S    string    `json:"s"`    // 字符串值(当Type为STRING时有效)
	A    []*Value  `json:"a"`    // 数组值(当Type为ARRAY时有效)
	O    []Member  `json:"o"`    // 对象值(当Type为OBJECT时有效)
}

Value 表示一个JSON值

func DefaultCircularReplacer

func DefaultCircularReplacer(path []string) *Value

DefaultCircularReplacer 默认循环引用替换器

func FindObjectKey

func FindObjectKey(v *Value, key string) (*Value, bool)

FindObjectKey 根据键名在对象中查找对应值,如果找到返回值和true,否则返回nil和false

func GetArrayElement

func GetArrayElement(v *Value, index int) *Value

GetArrayElement 获取JSON数组的元素

func GetObjectValue

func GetObjectValue(v *Value, index int) *Value

GetObjectValue 获取JSON对象的值

func GetObjectValueByKey

func GetObjectValueByKey(v *Value, key string) *Value

GetObjectValueByKey 根据键获取JSON对象的值

func GetValueByPointer

func GetValueByPointer(v *Value, pointerStr string) (*Value, error)

GetValueByPointer 使用JSON指针获取值

func InsertArrayElement

func InsertArrayElement(v *Value, index int) *Value

InsertArrayElement 在指定位置插入元素,并返回该元素

func PushBackArrayElement

func PushBackArrayElement(v *Value) *Value

PushBackArrayElement 在数组末尾添加一个新元素,并返回该元素

func SetObjectValue

func SetObjectValue(v *Value, key string) *Value

SetObjectValue 设置对象的键值对,如果键已存在则返回其值指针,否则添加新的键值对并返回新值指针

func (Value) String

func (v Value) String() string

String 返回Value的字符串表示

type ValueType

type ValueType int

ValueType 表示JSON值的类型

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

JSON值类型常量

func GetType

func GetType(v *Value) ValueType

GetType 获取JSON值的类型

Jump to

Keyboard shortcuts

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