364 lines
9.7 KiB
Go
364 lines
9.7 KiB
Go
// Package gjson_hack ...
|
|
//
|
|
// Description : gjson_hack ...
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 2024-11-27 11:58
|
|
package gjson_hack
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/tidwall/gjson"
|
|
)
|
|
|
|
// newDefaultPathOption 默认路径展开选项
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 12:06 2024/11/27
|
|
func newDefaultPathOption() *PathOption {
|
|
return &PathOption{
|
|
UnfoldArray: true,
|
|
MaxDeep: 0,
|
|
OnlyFinalPath: false,
|
|
}
|
|
}
|
|
|
|
// newPathResult ...
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 12:17 2024/11/27
|
|
func newPathResult() *PathResult {
|
|
return &PathResult{
|
|
List: make([]string, 0),
|
|
ValueTable: make(map[string]gjson.Result),
|
|
}
|
|
}
|
|
|
|
// Path 查看全部路径
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 12:03 2024/11/27
|
|
func Path(jsonStr string, pathOption *PathOption) (*PathResult, error) {
|
|
gjsonResult := gjson.Parse(jsonStr)
|
|
pathResult := newPathResult()
|
|
if nil == pathOption {
|
|
pathOption = newDefaultPathOption()
|
|
}
|
|
doExpandPath(gjsonResult, "", true, pathOption, pathResult)
|
|
return pathResult, nil
|
|
}
|
|
|
|
// doExpandPath 展开路径
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 14:57 2024/11/27
|
|
func doExpandPath(gjsonResult gjson.Result, rootPath string, hasChildren bool, pathOption *PathOption, pathResult *PathResult) {
|
|
if gjsonResult.IsObject() {
|
|
// 处理object
|
|
gjsonResult.ForEach(func(key, value gjson.Result) bool {
|
|
newRootPath := ""
|
|
if len(rootPath) == 0 {
|
|
newRootPath = getPathKey(key.String())
|
|
} else {
|
|
newRootPath = rootPath + "." + getPathKey(key.String())
|
|
}
|
|
if value.IsArray() || value.IsObject() {
|
|
if !pathOption.OnlyFinalPath {
|
|
// 中间key路径也存储下来
|
|
pathResult.List = append(pathResult.List, newRootPath)
|
|
pathResult.ValueTable[newRootPath] = value
|
|
}
|
|
doExpandPath(value, newRootPath, true, pathOption, pathResult)
|
|
} else {
|
|
pathResult.List = append(pathResult.List, newRootPath)
|
|
pathResult.ValueTable[newRootPath] = value
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
if gjsonResult.IsArray() {
|
|
arrayList := gjsonResult.Array()
|
|
if len(arrayList) == 0 {
|
|
pathResult.List = append(pathResult.List, rootPath)
|
|
pathResult.ValueTable[rootPath] = gjsonResult
|
|
return
|
|
}
|
|
if arrayList[0].IsObject() || arrayList[0].IsArray() {
|
|
// 每一项是对象或者数组
|
|
if pathOption.UnfoldArray {
|
|
// 展开数组
|
|
for _, itemRes := range arrayList {
|
|
// doExpandPath(itemRes, rootPath+"."+fmt.Sprintf("%v", idx), true, pathOption, pathResult)
|
|
doExpandPath(itemRes, rootPath+"."+ArrayIdxTpl, true, pathOption, pathResult)
|
|
}
|
|
} else {
|
|
// 不展开数组
|
|
doExpandPath(arrayList[0], rootPath+".#", true, pathOption, pathResult)
|
|
}
|
|
} else {
|
|
// 每一项是基础类型
|
|
pathResult.List = append(pathResult.List, rootPath)
|
|
pathResult.ValueTable[rootPath] = gjsonResult
|
|
return
|
|
}
|
|
}
|
|
if strings.HasSuffix(rootPath, ".#") || strings.HasSuffix(rootPath, ArrayIdxTpl) {
|
|
// 处理不展开类型数组
|
|
return
|
|
}
|
|
if pathOption.OnlyFinalPath && hasChildren {
|
|
// 仅记录最终完整路径, 中间路径不记录
|
|
return
|
|
}
|
|
if _, exist := pathResult.ValueTable[rootPath]; !exist {
|
|
pathResult.List = append(pathResult.List, rootPath)
|
|
pathResult.ValueTable[rootPath] = gjsonResult
|
|
}
|
|
return
|
|
}
|
|
|
|
// getPathKey ...
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 20:54 2024/12/4
|
|
func getPathKey(key string) string {
|
|
if !strings.Contains(key, ".") {
|
|
// 非特殊key
|
|
return key
|
|
}
|
|
if IsSpecialKey(key) {
|
|
// 已经是special key
|
|
return key
|
|
}
|
|
return SpecialKeyStart + key + SpecialKeyEnd
|
|
}
|
|
|
|
// IsArrayItemPath 是否为数组子项路径
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 15:49 2024/12/5
|
|
func IsArrayItemPath(path string) bool {
|
|
return strings.Contains(path, ArrayIdxTpl)
|
|
}
|
|
|
|
// ExpandArrayPath 根据真实数据展开数组路径
|
|
//
|
|
// 路径中若是包含 {{idx}} 占位符, 说明是需要展开的数组, 根据数组的实际数据, 进行数组的逐级展开
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 11:13 2024/11/29
|
|
func ExpandArrayPath(jsonStr string, pathConfig string, mapConfig string, expendArrayResult *ExpendArrayResult) error {
|
|
if nil == expendArrayResult {
|
|
return errors.New("expendArrayResult can not be nil")
|
|
}
|
|
if nil == expendArrayResult.PathList {
|
|
expendArrayResult.PathList = make([]string, 0)
|
|
}
|
|
if nil == expendArrayResult.PathMap {
|
|
expendArrayResult.PathMap = make(map[string]string)
|
|
}
|
|
if len(mapConfig) == 0 {
|
|
mapConfig = pathConfig
|
|
}
|
|
if !IsArrayItemPath(pathConfig) {
|
|
// 不是数组模板配置, 无需展开
|
|
expendArrayResult.PathList = append(expendArrayResult.PathList, pathConfig)
|
|
expendArrayResult.PathMap[pathConfig] = mapConfig
|
|
return nil
|
|
}
|
|
mapConfigArr := strings.Split(mapConfig, ArrayIdxTpl)
|
|
mapConfigArr[0] = strings.TrimSuffix(mapConfigArr[0], ".")
|
|
mapSuffixPathTpl := strings.TrimPrefix(strings.Join(mapConfigArr[1:], ArrayIdxTpl), ".")
|
|
|
|
pathConfigArr := strings.Split(pathConfig, ArrayIdxTpl)
|
|
pathConfigArr[0] = strings.TrimSuffix(pathConfigArr[0], ".")
|
|
suffixPathTpl := strings.TrimPrefix(strings.Join(pathConfigArr[1:], ArrayIdxTpl), ".")
|
|
|
|
if len(mapConfigArr) != len(pathConfigArr) {
|
|
return errors.New("mapConfig depth not equal pathConfig deep")
|
|
}
|
|
|
|
valueResult := Get(gjson.Parse(jsonStr), pathConfigArr[0])
|
|
if !valueResult.Exists() {
|
|
// 路径不存在, 无需设置具体值
|
|
return nil
|
|
}
|
|
if !valueResult.IsArray() {
|
|
// 不是数组,不要继续再向后展开了
|
|
expendArrayResult.PathList = append(expendArrayResult.PathList, pathConfigArr[0])
|
|
expendArrayResult.PathMap[pathConfigArr[0]] = mapConfigArr[0]
|
|
return nil
|
|
}
|
|
// 继续展开子项
|
|
for idx, _ := range valueResult.Array() {
|
|
idxStr := fmt.Sprintf("%v", idx)
|
|
if err := ExpandArrayPath(jsonStr, pathConfigArr[0]+"."+idxStr+"."+suffixPathTpl, mapConfigArr[0]+"."+idxStr+"."+mapSuffixPathTpl, expendArrayResult); nil != err {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsObject 判断是否是对象, 兼容序列化之后的对象
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 18:04 2024/12/1
|
|
func IsObject(gjsonResult gjson.Result) bool {
|
|
return gjsonResult.IsObject() || gjson.Parse(gjsonResult.String()).IsObject()
|
|
}
|
|
|
|
// IsArray 判断是否为数组
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 18:05 2024/12/1
|
|
func IsArray(gjsonResult gjson.Result) bool {
|
|
return gjsonResult.IsArray() || gjson.Parse(gjsonResult.String()).IsArray()
|
|
}
|
|
|
|
// Object 兼容序列化之后的对象
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 18:09 2024/12/1
|
|
func Object(gjsonResult gjson.Result) gjson.Result {
|
|
res := gjson.Parse(gjsonResult.String())
|
|
if res.IsObject() {
|
|
return res
|
|
}
|
|
return gjsonResult
|
|
}
|
|
|
|
// Array ...
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 18:09 2024/12/1
|
|
func Array(gjsonResult gjson.Result) gjson.Result {
|
|
res := gjson.Parse(gjsonResult.String())
|
|
if res.IsArray() {
|
|
return res
|
|
}
|
|
return gjsonResult
|
|
}
|
|
|
|
// Result ...
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 18:12 2024/12/1
|
|
func Result(gjsonResult gjson.Result) gjson.Result {
|
|
if IsObject(gjsonResult) {
|
|
return gjsonResult
|
|
}
|
|
if IsArray(gjsonResult) {
|
|
return Array(gjsonResult)
|
|
}
|
|
return Object(gjsonResult)
|
|
}
|
|
|
|
// Get 主要解决 key 中包含 . 的问题
|
|
//
|
|
// 如 : {"person.name": "test"} 如何将 person.name 整体作为一个字段而非两个层级
|
|
//
|
|
// 解决方案 :
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 20:17 2024/12/4
|
|
func Get(gjsonResult gjson.Result, path string) gjson.Result {
|
|
if len(path) == 0 {
|
|
return gjsonResult
|
|
}
|
|
if !gjsonResult.Exists() {
|
|
return gjsonResult
|
|
}
|
|
if !gjsonResult.IsObject() && !gjsonResult.IsArray() {
|
|
return gjsonResult.Get(path)
|
|
}
|
|
if !IsSpecialPath(path) {
|
|
// 不是特殊路径
|
|
return gjsonResult.Get(path)
|
|
}
|
|
pathArr := strings.Split(path, ".")
|
|
normalPathList := make([]string, 0)
|
|
specialKeyList := make([]string, 0)
|
|
specialKeyHasStart := false
|
|
for idx, item := range pathArr {
|
|
if strings.HasPrefix(item, SpecialKeyStart) {
|
|
specialKeyHasStart = true
|
|
specialKeyList = append(specialKeyList, item)
|
|
continue
|
|
}
|
|
if strings.HasSuffix(item, SpecialKeyEnd) {
|
|
specialKeyHasStart = false
|
|
specialKeyList = append(specialKeyList, item)
|
|
sourceResult := gjsonResult
|
|
if len(normalPathList) > 0 {
|
|
sourceResult = gjsonResult.Get(strings.Join(normalPathList, "."))
|
|
}
|
|
realKeyName := GetRealKeyName(strings.Join(specialKeyList, "."))
|
|
return Get(sourceResult.Map()[realKeyName], strings.Join(pathArr[idx+1:], "."))
|
|
}
|
|
if specialKeyHasStart {
|
|
specialKeyList = append(specialKeyList, item)
|
|
continue
|
|
}
|
|
normalPathList = append(normalPathList, item)
|
|
}
|
|
return gjsonResult
|
|
}
|
|
|
|
// GetMany 兼容 gjson GetMany 方法
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 16:32 2024/12/5
|
|
func GetMany(jsonStr string, pathList ...string) []gjson.Result {
|
|
res := make([]gjson.Result, 0)
|
|
gjsonResult := gjson.Parse(jsonStr)
|
|
for _, itemPath := range pathList {
|
|
res = append(res, Get(gjsonResult, itemPath))
|
|
}
|
|
return res
|
|
}
|
|
|
|
// IsSpecialPath 判断传入的是否为特殊路径
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 20:27 2024/12/4
|
|
func IsSpecialPath(path string) bool {
|
|
return strings.Contains(path, SpecialKeyStart) && strings.Contains(path, SpecialKeyEnd)
|
|
}
|
|
|
|
// IsSpecialKey 判断是否特殊key
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 20:29 2024/12/4
|
|
func IsSpecialKey(key string) bool {
|
|
return strings.HasPrefix(key, SpecialKeyStart) && strings.HasSuffix(key, SpecialKeyEnd)
|
|
}
|
|
|
|
// GetRealKeyName ...
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 20:31 2024/12/4
|
|
func GetRealKeyName(key string) string {
|
|
return strings.TrimSuffix(strings.TrimPrefix(key, SpecialKeyStart), SpecialKeyEnd)
|
|
}
|