读取指定路径数据
约 1060 字大约 4 分钟
2025-05-04
场景分析
在进行具体实现之前,我们需要先分析一下我们可能遇到的场景有哪些:
- 判断一个路径是否存在
- 读取指定路径的数据, 数据路径层级层层嵌套
- 读取指定路径的数据, 并进行类型转换
- 提取数组中的某一个key, 形成一个新的数组
示例如下:
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"name": "张三",
"age": 18,
"sex": "男",
"hobby": ["篮球", "足球", "羽毛球"],
"address": {
"province": "广东省",
"city": "深圳市",
"district": "南山区"
}
}`
// 判断一个路径是否存在
fmt.Println(gjson.Get(json, "name").Exists())
// 读取指定路径的数据, 数据路径层级层层嵌套
fmt.Println(gjson.Get(json, "address.district").String())
// 读取指定路径的数据, 并进行类型转换
fmt.Println(gjson.Get(json, "age").Int())
// 提取数组中的某一个key, 形成一个新的数组
fmt.Println(gjson.Get(json, "hobby.#.name").Array())
}
实现分析
以上示例是一个基于gjson的基础实现, 但是, 实际开发中, 不同的项目团队可能会使用不同的json库, 因此, 我们需要根据实际情况进行实现。而要支持各种方式的实现, 为了工程化后有统一的实现标准
,我们需要**抽象出json读取操作的基础能力是什么
**。 json操作的核心基础能力包括:
- 判断一个路径是否存在
- 在相关路径存在的情况下, 判断对应值的数据类型
- 读取一个路径的值, 并进行类型转换
基于以上核心能力, 来设计后续的实现
接口约束
如前文所述, gsjon已经实现了基础操作, 但是可能并不是赔项目团队的所有项目都使用gsjon, 因此, 我们需要对gjson的基础能力进行抽象, 并定义出一个接口, 来约束后续的实现。
package json
// Json 定义了json读取的基础能力
type Json interface {
// Exist 指定路径是否存在
Exist(dataPath string) bool
// IsNil 指定路径是否为nil
IsNil(dataPath string) bool
// Type 路径数据类型
Type(dataPath string) string
// Int 转换为int类型
Int(dataPath string) (int64, error)
// Uint 转换为uint类型
Uint(dataPath string) (uint64, error)
// Float 转换为float类型
Float(dataPath string) (float64, error)
// String 转换为string类型
String(dataPath string) (string, error)
// Bool 转换为bool类型
Bool(dataPath string) (bool, error)
// Map 转换为map
Map(dataPath string) (map[string]any, error)
// MapWithReceiver 通过指针接收
MapWithReceiver(dataPath string, receiver any) error
// Array 转换为数组
Array(dataPath string) ([]any, error)
// ArrayWithReceiver 通过指针接收
ArrayWithReceiver(dataPath string, receiver any) error
// Value 自适应类型数据读取
Value(dataPath string, dataType string, defaultValue any) (any, error)
}
关于类型转换的说明
为什么独立定义各个类型的转换方法, 而不用泛型实现?
如介绍中所说, 我们是在个人网关基础之上来实现, 并且在有限满足网关本身需求之上, 尽可能泛场景通用, 而网关的类型转换是一个复杂的过程, 因此, 我们需要独立定义各个类型的转换方法, 而不用泛型实现。
在问题一的基础之上, 为什么不定义其他类型的转换方法, 如: 没有int8?
int / uint / float 这类具有多精度的类型, 为了简化操作, 我们只定义了int64 / uint64 / float64 最高精度的转换方法。
具体实现
具体实现不做赘述, 可以以上参考代码。此处只针对上述代码实现中需要注意的内容做重点说明:
对于数组, 提取某一个字段生成新的数组如何实现?
上述实现是基于gjson的实现, 因此, 我们可以直接使用gjson的语法来实现, 如:
hobby.#.name
即可实现。map/array 数据类型转换
基于内置json库实现时,
开启UseNumber选项, 避免精度丢失
, 具体的精度问题可以使用一下数据进行验证, 查看开启与不开启UseNumber选项的差异:testData := map[string]any{ "big_number": 137806259135131648, }