Compare commits

..

22 Commits

Author SHA1 Message Date
b8c8c9d433 Merge pull request 'gjson+sjson hack升级' (#8) from feature/sjson_hack into master
Reviewed-on: #8
2024-12-05 18:50:07 +08:00
96ff376470 sjson支持设置key中包含 . 字符 2024-12-05 18:49:04 +08:00
f61f9b28b1 增加兼容的GetMany方法 2024-12-05 16:34:29 +08:00
f88826411f add test 2024-12-05 16:14:10 +08:00
df6e554d03 修复Get方法的BUG + 升级基于实际数据展开数据组真实路径的方法ExpandArrayPath 2024-12-05 16:09:18 +08:00
48639ba59a gjson读取参数支持key名称中包含. 2024-12-04 20:50:13 +08:00
355144b623 优化Result 2024-12-04 20:15:42 +08:00
ee2326af91 Merge pull request 'feature/upgrade_filter' (#7) from feature/upgrade_filter into master
Reviewed-on: #7
2024-12-02 14:35:29 +08:00
5b509aaee7 数据类型处理支持对指针的处理 2024-12-02 14:34:03 +08:00
5fc2e0a613 优化map数据的处理 2024-12-02 14:30:43 +08:00
7214767a90 filter.getValue => gjson_hack.Value 2024-12-02 11:36:03 +08:00
4a318f3638 增加ma[any]any/sliceSlice数据处理 2024-12-01 18:25:27 +08:00
d25e579ecf 支持将序列化之后的数组、对象字符串转回数组、对象 2024-12-01 18:14:34 +08:00
5ae1841023 优化[]map[string]any处理 2024-12-01 15:46:16 +08:00
d466b7526d 优化slice处理 2024-12-01 15:10:48 +08:00
c10486b403 修复潜在的丢失精度问题 2024-11-30 22:04:29 +08:00
8cca556d6f 优化error 2024-11-30 21:53:28 +08:00
68d3d76062 优化数据过滤处理 2024-11-30 19:07:55 +08:00
ce1e7fe9da 修复mapConfig处理错误问题 2024-11-30 17:31:55 +08:00
c1775552cc 更新单元测试 2024-11-30 17:22:16 +08:00
0ca808c4fb 数组按照真实层级展开后, 自动生成新的映射路径 2024-11-30 17:07:50 +08:00
cb23772664 增加 todo 待处理事项 2024-11-29 17:55:51 +08:00
8 changed files with 772 additions and 227 deletions

272
filter.go
View File

@ -10,12 +10,11 @@ package filter
import (
"encoding/json"
"fmt"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/serialize"
"git.zhangdeman.cn/zhangdeman/wrapper"
"reflect"
"strings"
"git.zhangdeman.cn/zhangdeman/json_filter/gjson_hack"
"git.zhangdeman.cn/zhangdeman/serialize"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
@ -53,44 +52,22 @@ type filter struct {
// Date : 11:59 2022/7/4
func (f *filter) Deal() error {
var (
err error
formatVal any
err error
)
for _, rule := range f.filterRuleList {
if len(rule.TargetPath) == 0 {
// 未配置目标路径则, 目标路径和源路径保持一致
rule.TargetPath = rule.SourcePath
}
if f.IsArray(rule) {
// 对于list的处理
// 对于list的处理, 展开层级, 并自动追加到f.filterRuleList 后面
if err = f.handleArray(rule); nil != err {
return err
}
continue
}
sourceResult := gjson.Get(f.sourceData, rule.SourcePath)
if formatVal, err = f.getValue(rule.DataType, sourceResult, rule.DefaultValue); nil != err {
return fmt.Errorf("%s = %v can not convert to %s : %s", rule.SourcePath, sourceResult.Value(), rule.DataType, err.Error())
}
if reflect.TypeOf(formatVal).Kind() == reflect.Map {
// 获取的数据是map类型, 处理数据覆盖
// eg : 配置如下两个规则 process.id(string) 、process(map[string]any)
// 若输入数据的process.id为int类型, 则格式化后的process.id必为 string, 应为 process.id 规则的控制更精细
gjsonVal := gjson.Get(f.formatResult, rule.TargetPath)
if gjsonVal.Exists() && gjsonVal.IsObject() {
var (
existRes = map[string]any{}
formatRes = map[string]any{}
)
// 已存在, 且是对象
_ = serialize.JSON.UnmarshalWithNumber([]byte(gjsonVal.String()), &existRes)
if err = serialize.JSON.Transition(formatVal, &formatRes); nil != err {
return errors.New("conflict data path config deal fail : " + err.Error())
}
for k, v := range existRes {
formatRes[k] = v
}
formatVal = formatRes // 重新赋值 formatVal
}
}
if f.formatResult, err = sjson.Set(f.formatResult, rule.TargetPath, formatVal); nil != err {
if err = f.setResult(rule); nil != err {
return err
}
}
@ -103,7 +80,7 @@ func (f *filter) Deal() error {
//
// Date : 17:48 2023/1/1
func (f *filter) IsArray(rule MapRule) bool {
return strings.Contains(rule.SourcePath, "[]")
return strings.Contains(rule.SourcePath, gjson_hack.ArrayIdxTpl)
}
// handleArray 处理数组(最复杂的场景)
@ -112,33 +89,71 @@ func (f *filter) IsArray(rule MapRule) bool {
//
// Date : 17:41 2023/1/1
func (f *filter) handleArray(rule MapRule) error {
// 对于list的处理, 展开层级, 并自动追加到f.filterRuleList 后面
var (
err error
)
sourcePathArray := strings.Split(rule.SourcePath, "[]")
for idx, item := range sourcePathArray {
sourcePathArray[idx] = strings.Trim(item, ".")
expendRes := &gjson_hack.ExpendArrayResult{
PathList: make([]string, 0),
PathMap: map[string]string{},
}
mapPathArray := strings.Split(strings.TrimRight(rule.TargetPath, ".[]"), "[]")
for idx, item := range mapPathArray {
mapPathArray[idx] = strings.Trim(item, ".")
if err = gjson_hack.ExpandArrayPath(f.sourceData, rule.SourcePath, rule.TargetPath, expendRes); nil != err {
return err
}
if len(sourcePathArray) != len(mapPathArray) {
if len(mapPathArray) != 1 {
return errors.New("map rule is invalid")
}
// 提取某一个list下的字段, 组成一个list
res := make([]string, 0)
if len(sourcePathArray[0]) == 0 {
f.getAllFinalData(&res, gjson.Parse(f.sourceData).Array(), sourcePathArray[1:])
} else {
f.getAllFinalData(&res, gjson.Get(f.sourceData, sourcePathArray[0]).Array(), sourcePathArray[1:])
}
if f.formatResult, err = sjson.Set(f.formatResult, mapPathArray[0], res); nil != err {
for _, itemPath := range expendRes.PathList {
if err = f.setResult(MapRule{
SourcePath: itemPath,
TargetPath: expendRes.PathMap[itemPath],
Required: rule.Required,
DataType: rule.DataType,
DefaultValue: rule.DefaultValue,
}); nil != err {
return err
}
return nil
}
return nil
}
// setResult 设置结果
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 19:03 2024/11/30
func (f *filter) setResult(rule MapRule) error {
var (
err error
formatVal any
)
sourceResult := gjson.Get(f.sourceData, rule.SourcePath)
if formatVal, err = gjson_hack.Value(rule.DataType, sourceResult, rule.DefaultValue); nil != err {
return fmt.Errorf("%s = %v can not convert to %s : %s", rule.SourcePath, sourceResult.Value(), rule.DataType, err.Error())
}
if reflect.TypeOf(formatVal).Kind() == reflect.Map {
// 获取的数据是map类型, 处理数据覆盖
// eg : 配置如下两个规则 process.id(string) 、process(map[string]any)
// 若输入数据的process.id为int类型, 则格式化后的process.id必为 string, 应为 process.id 规则的控制更精细
gjsonVal := gjson.Get(f.formatResult, rule.TargetPath)
if gjsonVal.Exists() && gjsonVal.IsObject() {
var (
existRes = map[string]any{}
formatRes = map[string]any{}
)
// 已存在, 且是对象
_ = serialize.JSON.UnmarshalWithNumber([]byte(gjsonVal.String()), &existRes)
if err = serialize.JSON.Transition(formatVal, &formatRes); nil != err {
return errors.New("conflict data path config deal fail : " + err.Error())
}
for k, v := range existRes {
formatRes[k] = v
}
formatVal = formatRes // 重新赋值 formatVal
}
}
if f.formatResult, err = sjson.Set(f.formatResult, rule.TargetPath, formatVal); nil != err {
return err
}
return nil
}
@ -190,156 +205,3 @@ func (f *filter) Parse(receiver interface{}) error {
}
return json.Unmarshal(f.Byte(), receiver)
}
// getValue 获取值
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:25 2022/7/4
func (f *filter) getValue(dataType consts.DataType, sourceValue gjson.Result, defaultValue string) (any, error) {
sourceValueStr := defaultValue
if sourceValue.Exists() {
str := sourceValue.String()
if len(str) > 0 {
sourceValueStr = str
}
}
strVal := wrapper.String(sourceValueStr)
switch dataType {
case consts.DataTypeInt:
intVal := strVal.ToInt()
return intVal.Value, intVal.Err
case consts.DataTypeUint:
uintVal := strVal.ToUint64()
return uintVal.Value, uintVal.Err
case consts.DataTypeBool:
boolVal := strVal.ToBool()
return boolVal.Value, boolVal.Err
case consts.DataTypeFloat:
floatVal := strVal.ToFloat64()
return floatVal.Value, floatVal.Err
case consts.DataTypeString:
return strVal.Value(), nil
case consts.DataTypeAny:
if sourceValue.Exists() {
return sourceValue.Value(), nil
}
return defaultValue, nil
case consts.DataTypeSliceAny:
// 任意类型的list
sliceVal := strVal.ToAnySlice()
return sliceVal.Value, sliceVal.Err
case consts.DataTypeSliceInt, consts.DataTypeSliceIntWithChar:
// 任意类型的list
if strings.HasPrefix(strVal.Value(), "[") && strings.HasPrefix(strVal.Value(), "]") {
// 序列化之后的数组
sliceVal := strVal.ToInt64Slice()
return sliceVal.Value, sliceVal.Err
}
// 分隔的数组
sliceVal := strVal.ToInt64Slice(",")
return sliceVal.Value, sliceVal.Err
case consts.DataTypeSliceUint, consts.DataTypeSliceUintWithChar:
// 任意类型的list
if strings.HasPrefix(strVal.Value(), "[") && strings.HasPrefix(strVal.Value(), "]") {
// 序列化之后的数组
sliceVal := strVal.ToUint64Slice()
return sliceVal.Value, sliceVal.Err
}
// 分隔的数组
sliceVal := strVal.ToUint64Slice(",")
return sliceVal.Value, sliceVal.Err
case consts.DataTypeSliceFloat, consts.DataTypeSliceFloatWithChar:
// 任意类型的list
if strings.HasPrefix(strVal.Value(), "[") && strings.HasPrefix(strVal.Value(), "]") {
// 序列化之后的数组
sliceVal := strVal.ToFloat64Slice()
return sliceVal.Value, sliceVal.Err
}
// 分隔的数组
sliceVal := strVal.ToFloat64Slice(",")
return sliceVal.Value, sliceVal.Err
case consts.DataTypeSliceBool, consts.DataTypeSliceBoolWithChar:
// 任意类型的list
if strings.HasPrefix(strVal.Value(), "[") && strings.HasPrefix(strVal.Value(), "]") {
// 序列化之后的数组
sliceVal := strVal.ToBoolSlice()
return sliceVal.Value, sliceVal.Err
}
// 分隔的数组
sliceVal := strVal.ToBoolSlice(",")
return sliceVal.Value, sliceVal.Err
case consts.DataTypeSliceString, consts.DataTypeSliceStringWithChar:
// 任意类型的list
if strings.HasPrefix(strVal.Value(), "[") && strings.HasPrefix(strVal.Value(), "]") {
// 序列化之后的数组
sliceVal := strVal.ToStringSlice()
return sliceVal.Value, sliceVal.Err
}
// 分隔的数组
sliceVal := strVal.ToStringSlice(",")
return sliceVal.Value, sliceVal.Err
case consts.DataTypeSliceSlice, consts.DataTypeMapAnyAny:
return nil, errors.New(consts.DataTypeSliceSlice.String() + " : data type is not support")
case consts.DataTypeSliceMapStringAny:
if !sourceValue.IsArray() {
return nil, errors.New("data type is not array")
}
var res []map[string]any
err := strVal.ToStruct(&res)
return res, err
case consts.DataTypeMapStrInt:
if !sourceValue.IsObject() {
return nil, errors.New("data type is not object")
}
var res map[string]int64
err := strVal.ToStruct(&res)
return res, err
case consts.DataTypeMapStrUint:
if !sourceValue.IsObject() {
return nil, errors.New("data type is not object")
}
var res map[string]uint64
err := strVal.ToStruct(&res)
return res, err
case consts.DataTypeMapStrFloat:
if !sourceValue.IsObject() {
return nil, errors.New("data type is not object")
}
var res map[string]float64
err := strVal.ToStruct(&res)
return res, err
case consts.DataTypeMapStrBool:
if !sourceValue.IsObject() {
return nil, errors.New("data type is not object")
}
var res map[string]bool
err := strVal.ToStruct(&res)
return res, err
case consts.DataTypeMapStrAny:
if !sourceValue.IsObject() {
return nil, errors.New("data type is not object")
}
var res map[string]any
err := strVal.ToStruct(&res)
return res, err
case consts.DataTypeMapStrStr:
if !sourceValue.IsObject() {
return nil, errors.New("data type is not object")
}
var res map[string]string
err := strVal.ToStruct(&res)
return res, err
case consts.DataTypeMapStrSlice:
if !sourceValue.IsObject() {
return nil, errors.New("data type is not object")
}
var res map[string][]any
err := strVal.ToStruct(&res)
return res, err
default:
return nil, errors.New(dataType.String() + " is not support!")
}
}

View File

@ -40,5 +40,11 @@ const (
//
// Date : 15:46 2024/11/29
type ExpendArrayResult struct {
PathList []string `json:"path_list"` // 路径列表
PathList []string `json:"path_list"` // 路径列表
PathMap map[string]string `json:"path_map"` // 数据源路径 => 目标路径的处理
}
const (
SpecialKeyStart = "{{#"
SpecialKeyEnd = "#}}"
)

15
gjson_hack/error.go Normal file
View File

@ -0,0 +1,15 @@
// Package gjson_hack ...
//
// Description : filter ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-11-30 19:14
package gjson_hack
import "errors"
var (
ErrDataIsNotObject = errors.New("data is not an object")
ErrDataIsNotArray = errors.New("data is not an array")
)

View File

@ -10,8 +10,9 @@ package gjson_hack
import (
"errors"
"fmt"
"github.com/tidwall/gjson"
"strings"
"github.com/tidwall/gjson"
)
// newDefaultPathOption 默认路径展开选项
@ -65,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 {
@ -124,30 +125,71 @@ 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<白茶清欢>
//
// Date : 11:13 2024/11/29
func ExpandArrayPath(jsonStr string, pathConfig string, expendArrayResult *ExpendArrayResult) error {
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 !strings.Contains(pathConfig, ArrayIdxTpl) {
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), ".")
valueResult := gjson.Parse(jsonStr).Get(pathConfigArr[0])
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
@ -155,14 +197,167 @@ func ExpandArrayPath(jsonStr string, pathConfig string, expendArrayResult *Expen
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, expendArrayResult); nil != err {
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)
}

View File

@ -10,8 +10,9 @@ package gjson_hack
import (
"encoding/json"
"fmt"
"github.com/tidwall/gjson"
"testing"
"github.com/tidwall/gjson"
)
func TestPath(t *testing.T) {
@ -95,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},
@ -133,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{
@ -162,8 +165,37 @@ func TestExpandArrayPath(t *testing.T) {
}
byteData, _ := json.Marshal(mapData)
jsonStr := string(byteData)
// fmt.Println(jsonStr)
var pathExpendRes = &ExpendArrayResult{PathList: nil}
ExpandArrayPath(jsonStr, "user_list.{{idx}}.{{idx}}.age", pathExpendRes)
fmt.Println(pathExpendRes)
var pathExpendRes = &ExpendArrayResult{
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 := ""
for _, item := range pathExpendRes.PathList {
// 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())
}
}
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())
}

View File

@ -9,8 +9,12 @@ package gjson_hack
import (
"errors"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/serialize"
"git.zhangdeman.cn/zhangdeman/util"
"git.zhangdeman.cn/zhangdeman/wrapper"
"github.com/tidwall/gjson"
"strings"
)
// Number 结果转换为数字(int64 / uint64 / float64)
@ -69,3 +73,348 @@ func Float64(gjsonResult gjson.Result) (float64, error) {
}
return float64Result, nil
}
// String 获取字符串值
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:55 2024/12/1
func String(sourceValue gjson.Result, defaultValue string) string {
sourceValueStr := defaultValue
if sourceValue.Exists() {
str := sourceValue.String()
if len(str) > 0 {
sourceValueStr = str
}
}
return sourceValueStr
}
// MapAnyAny ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:48 2024/12/1
func MapAnyAny(sourceValue gjson.Result) (map[string]any, error) {
if !sourceValue.IsObject() {
return nil, ErrDataIsNotObject
}
res := make(map[string]any)
sourceValue.ForEach(func(key, value gjson.Result) bool {
res[key.String()] = value
return true
})
return res, nil
}
// Any 获取任意类型的值
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:00 2024/12/1
func Any(sourceValue gjson.Result) (any, error) {
if !sourceValue.Exists() {
return nil, nil
}
// 可能存在精度丢失, 原因 : gjson.Value 内置的转换, int64 超过一定大小会存在丢失精度问题
if sourceValue.Num > 0 || sourceValue.Num < 0 {
// 说明是数字
var res float64
if err := util.ConvertAssign(&res, sourceValue.String()); nil != err {
return nil, err
}
return res, nil
}
if sourceValue.IsObject() {
return MapAnyAny(sourceValue)
}
if sourceValue.IsArray() {
return SliceAny(sourceValue)
}
return sourceValue.Value(), nil
}
// getRealDataType ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:28 2024/12/1
func getRealDataType(dataType consts.DataType, sourceValue gjson.Result) consts.DataType {
// 指针数据类型, 转换为普通数据类型处理
dataType = consts.DataType(strings.TrimLeft(dataType.String(), "*"))
if dataType == consts.DataTypeAny {
if sourceValue.IsObject() {
dataType = consts.DataTypeMapAnyAny
} else if sourceValue.IsArray() {
dataType = consts.DataTypeSliceAny
} else {
if sourceValue.Num != 0 {
dataType = consts.DataTypeFloat
}
}
}
return dataType
}
// SliceAny ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:53 2024/12/1
func SliceAny(gjsonResult gjson.Result) ([]any, error) {
if gjsonResult.Value() == nil {
return nil, nil
}
strVal := strings.TrimSpace(gjsonResult.String())
// 任意类型的list
if strings.HasPrefix(strVal, "[") && strings.HasSuffix(strVal, "]") {
// 序列化之后的数组
sliceVal := wrapper.String(strVal).ToAnySlice()
return sliceVal.Value, sliceVal.Err
}
// 分隔的数组
sliceVal := wrapper.String(strVal).ToAnySlice(",")
return sliceVal.Value, sliceVal.Err
}
// SliceInt 获取int list
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:43 2024/12/1
func SliceInt(gjsonResult gjson.Result) ([]int64, error) {
if gjsonResult.Value() == nil {
return nil, nil
}
strVal := strings.TrimSpace(gjsonResult.String())
// 任意类型的list
if strings.HasPrefix(strVal, "[") && strings.HasSuffix(strVal, "]") {
// 序列化之后的数组
sliceVal := wrapper.String(strVal).ToInt64Slice()
return sliceVal.Value, sliceVal.Err
}
// 分隔的数组
sliceVal := wrapper.String(strVal).ToInt64Slice(",")
return sliceVal.Value, sliceVal.Err
}
// SliceUint ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:58 2024/12/1
func SliceUint(gjsonResult gjson.Result) ([]uint64, error) {
if gjsonResult.Value() == nil {
return nil, nil
}
strVal := strings.TrimSpace(gjsonResult.String())
// 任意类型的list
if strings.HasPrefix(strVal, "[") && strings.HasSuffix(strVal, "]") {
// 序列化之后的数组
sliceVal := wrapper.String(strVal).ToUint64Slice()
return sliceVal.Value, sliceVal.Err
}
// 分隔的数组
sliceVal := wrapper.String(strVal).ToUint64Slice(",")
return sliceVal.Value, sliceVal.Err
}
// SliceFloat ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:59 2024/12/1
func SliceFloat(gjsonResult gjson.Result) ([]float64, error) {
if gjsonResult.Value() == nil {
return nil, nil
}
strVal := strings.TrimSpace(gjsonResult.String())
// 任意类型的list
if strings.HasPrefix(strVal, "[") && strings.HasSuffix(strVal, "]") {
// 序列化之后的数组
sliceVal := wrapper.String(strVal).ToFloat64Slice()
return sliceVal.Value, sliceVal.Err
}
// 分隔的数组
sliceVal := wrapper.String(strVal).ToFloat64Slice(",")
return sliceVal.Value, sliceVal.Err
}
// SliceBool ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:00 2024/12/1
func SliceBool(gjsonResult gjson.Result) ([]bool, error) {
if gjsonResult.Value() == nil {
return nil, nil
}
strVal := strings.TrimSpace(gjsonResult.String())
// 任意类型的list
if strings.HasPrefix(strVal, "[") && strings.HasSuffix(strVal, "]") {
// 序列化之后的数组
sliceVal := wrapper.String(strVal).ToBoolSlice()
return sliceVal.Value, sliceVal.Err
}
// 分隔的数组
sliceVal := wrapper.String(strVal).ToBoolSlice(",")
return sliceVal.Value, sliceVal.Err
}
// SliceString ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:03 2024/12/1
func SliceString(gjsonResult gjson.Result) ([]string, error) {
if gjsonResult.Value() == nil {
return nil, nil
}
strVal := strings.TrimSpace(gjsonResult.String())
// 任意类型的list
if strings.HasPrefix(strVal, "[") && strings.HasSuffix(strVal, "]") {
// 序列化之后的数组
sliceVal := wrapper.String(strVal).ToStringSlice()
return sliceVal.Value, sliceVal.Err
}
// 分隔的数组
sliceVal := wrapper.String(strVal).ToStringSlice(",")
return sliceVal.Value, sliceVal.Err
}
// SliceMapStringAny ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:41 2024/12/1
func SliceMapStringAny(gjsonResult gjson.Result) ([]map[string]any, error) {
if gjsonResult.Value() == nil {
return nil, nil
}
strVal := strings.TrimSpace(gjsonResult.String())
// 任意类型的list
if !strings.HasPrefix(strVal, "[") || !strings.HasSuffix(strVal, "]") {
return nil, ErrDataIsNotArray
}
var res []map[string]any
if err := serialize.JSON.UnmarshalWithNumber([]byte(strVal), &res); nil != err {
return nil, err
}
return res, nil
}
// SliceSlice ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:44 2024/12/1
func SliceSlice(gjsonResult gjson.Result) ([][]any, error) {
if gjsonResult.Value() == nil {
return nil, nil
}
strVal := strings.TrimSpace(gjsonResult.String())
// 任意类型的list
if !strings.HasPrefix(strVal, "[") || !strings.HasSuffix(strVal, "]") {
return nil, ErrDataIsNotArray
}
var res [][]any
if err := serialize.JSON.UnmarshalWithNumber([]byte(strVal), &res); nil != err {
return nil, err
}
return res, nil
}
// MapStrAny 获取任意map类型的结果
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:26 2024/12/2
func MapStrAny[T string | int64 | uint64 | bool | float64 | []any | map[string]any | any](gjsonResult gjson.Result) (map[string]T, error) {
if !gjsonResult.IsObject() {
return nil, ErrDataIsNotObject
}
var res map[string]T
if err := serialize.JSON.UnmarshalWithNumber([]byte(gjsonResult.String()), &res); nil != err {
return nil, err
}
return res, nil
}
// Value 获取指定的值
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:52 2024/12/1
func Value(dataType consts.DataType, sourceValue gjson.Result, defaultValue any) (any, error) {
if !sourceValue.Exists() {
return defaultValue, nil
}
// 归一化处理对象、数组等
sourceValue = Result(sourceValue)
dataType = getRealDataType(dataType, sourceValue)
strVal := wrapper.String(sourceValue.String())
switch dataType {
case consts.DataTypeInt:
return Int(sourceValue)
case consts.DataTypeUint:
return Uint(sourceValue)
case consts.DataTypeFloat:
return Float64(sourceValue)
case consts.DataTypeBool:
boolVal := strVal.ToBool()
return boolVal.Value, boolVal.Err
case consts.DataTypeString:
return sourceValue.String(), nil
case consts.DataTypeAny:
return sourceValue.Value(), nil
case consts.DataTypeSliceAny:
// 任意类型的list
sliceVal := strVal.ToAnySlice()
return sliceVal.Value, sliceVal.Err
case consts.DataTypeSliceInt, consts.DataTypeSliceIntWithChar:
// 任意类型的list
return SliceInt(sourceValue)
case consts.DataTypeSliceUint, consts.DataTypeSliceUintWithChar:
// 任意类型的list
return SliceUint(sourceValue)
case consts.DataTypeSliceFloat, consts.DataTypeSliceFloatWithChar:
// 任意类型的list
return SliceFloat(sourceValue)
case consts.DataTypeSliceBool, consts.DataTypeSliceBoolWithChar:
// 任意类型的list
return SliceBool(sourceValue)
case consts.DataTypeSliceString, consts.DataTypeSliceStringWithChar:
// 任意类型的list
return SliceString(sourceValue)
case consts.DataTypeSliceSlice:
return SliceSlice(sourceValue)
case consts.DataTypeMapAnyAny:
return MapAnyAny(sourceValue)
case consts.DataTypeSliceMapStringAny:
return SliceMapStringAny(sourceValue)
case consts.DataTypeMapStrInt:
return MapStrAny[int64](sourceValue)
case consts.DataTypeMapStrUint:
return MapStrAny[uint64](sourceValue)
case consts.DataTypeMapStrFloat:
return MapStrAny[float64](sourceValue)
case consts.DataTypeMapStrBool:
return MapStrAny[bool](sourceValue)
case consts.DataTypeMapStrAny:
return MapStrAny[any](sourceValue)
case consts.DataTypeMapStrStr:
return MapStrAny[string](sourceValue)
case consts.DataTypeMapStrSlice:
return MapStrAny[[]any](sourceValue)
default:
return nil, errors.New("data_type = `" + dataType.String() + "` is not support!")
}
}

68
sjson_hack/set.go Normal file
View File

@ -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
}

18
sjson_hack/set_test.go Normal file
View File

@ -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)
}