diff --git a/gjson_hack/define.go b/gjson_hack/define.go index 577d477..bf9fb3c 100644 --- a/gjson_hack/define.go +++ b/gjson_hack/define.go @@ -43,3 +43,8 @@ type ExpendArrayResult struct { PathList []string `json:"path_list"` // 路径列表 PathMap map[string]string `json:"path_map"` // 数据源路径 => 目标路径的处理 } + +const ( + SpecialKeyStart = "{{#" + SpecialKeyEnd = "#}}" +) diff --git a/gjson_hack/path.go b/gjson_hack/path.go index 2e7995a..85e3f7b 100644 --- a/gjson_hack/path.go +++ b/gjson_hack/path.go @@ -66,9 +66,9 @@ func doExpandPath(gjsonResult gjson.Result, rootPath string, hasChildren bool, p gjsonResult.ForEach(func(key, value gjson.Result) bool { newRootPath := "" if len(rootPath) == 0 { - newRootPath = key.String() + newRootPath = getPathKey(key.String()) } else { - newRootPath = rootPath + "." + key.String() + newRootPath = rootPath + "." + getPathKey(key.String()) } if value.IsArray() || value.IsObject() { if !pathOption.OnlyFinalPath { @@ -125,9 +125,35 @@ func doExpandPath(gjsonResult gjson.Result, rootPath string, hasChildren bool, p 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}} 占位符, 说明是需要展开的数组, 根据数组的时机数据, 进行数组的逐级展开 +// 路径中若是包含 {{idx}} 占位符, 说明是需要展开的数组, 根据数组的实际数据, 进行数组的逐级展开 // // Author : go_developer@163.com<白茶清欢> // @@ -145,7 +171,7 @@ func ExpandArrayPath(jsonStr string, pathConfig string, mapConfig string, expend if len(mapConfig) == 0 { mapConfig = pathConfig } - if !strings.Contains(pathConfig, ArrayIdxTpl) { + if !IsArrayItemPath(pathConfig) { // 不是数组模板配置, 无需展开 expendArrayResult.PathList = append(expendArrayResult.PathList, pathConfig) expendArrayResult.PathMap[pathConfig] = mapConfig @@ -163,7 +189,7 @@ func ExpandArrayPath(jsonStr string, pathConfig string, mapConfig string, expend return errors.New("mapConfig depth not equal pathConfig deep") } - valueResult := gjson.Parse(jsonStr).Get(pathConfigArr[0]) + valueResult := Get(gjson.Parse(jsonStr), pathConfigArr[0]) if !valueResult.Exists() { // 路径不存在, 无需设置具体值 return nil @@ -240,5 +266,98 @@ func Result(gjsonResult gjson.Result) gjson.Result { 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) +} diff --git a/gjson_hack/path_test.go b/gjson_hack/path_test.go index 1848b11..5654f1e 100644 --- a/gjson_hack/path_test.go +++ b/gjson_hack/path_test.go @@ -12,8 +12,6 @@ import ( "fmt" "testing" - "github.com/tidwall/sjson" - "github.com/tidwall/gjson" ) @@ -98,13 +96,14 @@ func TestPathOnlyFinallyPathWithUnfoldArray(t *testing.T) { "company_info": map[string]any{ "address": "Beijing", "email": "xxx@xxx.com", + "a.b": "cd", }, "sex": "man", "user_list": [][]map[string]any{ []map[string]any{ - {"name": "zhang", "age": 10}, - {"name": "li", "age": 20}, - {"name": "wang", "age": 30}, + {"name": "zhang", "age": 10, "e.f": "g"}, + {"name": "li", "age": 20, "e.f": "g"}, + {"name": "wang", "age": 30, "e.f": "g"}, }, []map[string]any{ {"name": "zhang", "age": 10}, @@ -136,13 +135,14 @@ func TestPathOnlyFinallyPathWithUnfoldArray(t *testing.T) { func TestExpandArrayPath(t *testing.T) { mapData := map[string]any{ "person_list": []map[string]any{ - {"name": "zhang", "age": 10}, - {"name": "li", "age": 20}, - {"name": "wang", "age": 30}, + {"name": "zhang", "age": 10, "a.b": "people_name"}, + {"name": "li", "age": 20, "a.b": "people_name"}, + {"name": "wang", "age": 30, "a.b": "people_name"}, }, "company_info": map[string]any{ - "address": "Beijing", - "email": "xxx@xxx.com", + "address": "Beijing", + "email": "xxx@xxx.com", + "level.a.b": "deep level", }, "sex": "man", "user_list": [][]map[string]any{ @@ -170,12 +170,32 @@ func TestExpandArrayPath(t *testing.T) { PathList: nil, PathMap: nil, } + ExpandArrayPath(jsonStr, "company_info.{{#level.a.b#}}", "company_info.a-b", pathExpendRes) + ExpandArrayPath(jsonStr, "person_list.{{idx}}.{{#a.b#}}", "person_list.{{idx}}.a-b", pathExpendRes) ExpandArrayPath(jsonStr, "user_list.{{idx}}.{{idx}}.age", "a.{{idx}}.{{idx}}.b", pathExpendRes) ExpandArrayPath(jsonStr, "user_list.{{idx}}.{{idx}}.name", "e.{{idx}}.{{idx}}.c", pathExpendRes) ExpandArrayPath(jsonStr, "user_list.{{idx}}.{{idx}}.sex", "f.{{idx}}.{{idx}}.c", pathExpendRes) - res := "" + // res := "" for _, item := range pathExpendRes.PathList { - res, _ = sjson.Set(res, pathExpendRes.PathMap[item], gjson.Get(jsonStr, item).Value()) + // fmt.Println(item, pathExpendRes.PathMap[item]) + fmt.Println(item, pathExpendRes.PathMap[item], Get(gjson.Parse(jsonStr), item).String()) + // res, _ = sjson.Set(res, pathExpendRes.PathMap[item], Get(gjson.Parse(jsonStr), item).Value()) } - fmt.Println(res) +} + +func TestGet(t *testing.T) { + mapData := map[string]any{ + "person.name": "test", + "test": map[string]any{ + "a.b": "c", + "d.e": map[string]any{ + "e.f": "g", + }, + }, + } + byteData, _ := json.Marshal(mapData) + gjsonResult := gjson.ParseBytes(byteData) + fmt.Println(Get(gjsonResult, "{{#person.name#}}").String()) + fmt.Println(Get(gjsonResult, "test.{{#a.b#}}").String()) + fmt.Println(Get(gjsonResult, "test.{{#d.e#}}.{{#e.f#}}").String()) } diff --git a/sjson_hack/set.go b/sjson_hack/set.go new file mode 100644 index 0000000..809fb11 --- /dev/null +++ b/sjson_hack/set.go @@ -0,0 +1,68 @@ +// Package sjson_hack ... +// +// Description : sjson_hack ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-12-03 11:36 +package sjson_hack + +import ( + "errors" + "fmt" + "git.zhangdeman.cn/zhangdeman/json_filter/gjson_hack" + "git.zhangdeman.cn/zhangdeman/wrapper" + "github.com/tidwall/sjson" + "regexp" + "strings" +) + +var ( + reg = regexp.MustCompile(`({\{\#.*?\#\}\})`) +) + +// Set 设置路径的值 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 11:36 2024/12/3 +func Set(jsonRes string, path string, value any) (string, error) { + fmt.Println(jsonRes, value) + var ( + err error + res string = jsonRes + ) + + // 包含特殊字符串, 匹配出特殊字符串 + specialKeyList := getSpecialKeyList(path) + specialKeyTale := map[string]string{} + for _, item := range specialKeyList { + // 替换掉占位字符串 + specialKeyTale[item] = wrapper.StringFromRandom(64, "").Md5().Value + path = strings.ReplaceAll(path, item, specialKeyTale[item]) + } + + if res, err = sjson.Set(res, path, value); nil != err { + return "", errors.New(path + " -> set json fail:" + err.Error()) + } + + // 将特殊字符串替换回来 + for sourceKey, convertKey := range specialKeyTale { + res = strings.ReplaceAll(res, convertKey, gjson_hack.GetRealKeyName(sourceKey)) + } + + return res, nil +} + +// getSpecialKeyList 获取特殊key列表 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 17:35 2024/12/5 +func getSpecialKeyList(path string) []string { + matchList := reg.FindAllString(path, -1) + if len(matchList) == 0 { + return make([]string, 0) + } + return matchList +} diff --git a/sjson_hack/set_test.go b/sjson_hack/set_test.go new file mode 100644 index 0000000..b492da9 --- /dev/null +++ b/sjson_hack/set_test.go @@ -0,0 +1,18 @@ +// Package sjson_hack ... +// +// Description : sjson_hack ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-12-05 17:32 +package sjson_hack + +import ( + "fmt" + "testing" +) + +func TestSet(t *testing.T) { + res, err := Set("{}", "{{#a.b#}}.c.{{#c.d#}}.e", "test") + fmt.Println(res, err) +}