Compare commits

...

86 Commits

Author SHA1 Message Date
2e1ece4c82 gjson_read优化类型判断 2025-05-24 18:56:28 +08:00
dfae2fda76 修复Number转换BUG 2025-05-24 18:50:31 +08:00
80e62625f2 升级 gjson_hack 2025-05-24 15:26:33 +08:00
b0db389ca3 Merge pull request 'getRealDataType尅性识别优化 + Value 泛解析支持更多数据类型' (#11) from feature/bug_fix into master
Reviewed-on: #11
2025-05-06 17:08:24 +08:00
a0c2fb37e3 getRealType适配未输入数据类型的情况 2025-05-06 17:07:45 +08:00
c403047e9f getRealDataType尅性识别优化 + Value 泛解析支持更多数据类型 2025-05-06 17:04:20 +08:00
777a5cb93c 所有操作加锁 2025-05-06 16:02:14 +08:00
691bee91a5 sjson_write支持删除数据 2025-05-06 15:42:24 +08:00
c7ce590e3b add bool support 2025-05-06 14:43:22 +08:00
6c2c68c53e Merge pull request '升级json操作, 升级为面向接口实现' (#10) from feature/upgrade_get_set into master
Reviewed-on: #10
2025-05-06 14:14:30 +08:00
38b2d74f35 remove debug code 2025-05-06 14:14:18 +08:00
a28bc364e4 优化filter逻辑 2025-05-06 14:10:38 +08:00
47726c20b8 SJsonWrite设置数据时, 支持做map合并 2025-05-06 12:39:48 +08:00
042aad254b IJsonRead支持Value, 泛读取数据 2025-05-06 12:22:26 +08:00
1124ff6a33 增加基于sjson实现的IJsonWrite 2025-05-06 12:07:58 +08:00
8489acbef8 增加基于gsjon实现的JsonRead 2025-05-06 11:51:50 +08:00
761058f8fe 增加IJsonRead与IJsonWrite接口约束 2025-05-06 10:58:20 +08:00
587a9bbe9d Merge pull request 'update go mod' (#9) from feature/upgrade into master
Reviewed-on: #9
2025-03-29 16:02:13 +08:00
50a8474e4d update go mod 2025-03-29 15:49:49 +08:00
28fbd1e820 fix 2025-03-29 15:47:56 +08:00
bbe5eb089f update go mod 2025-03-29 15:46:31 +08:00
786c03293a upgrade wrapper 2025-03-21 18:30:29 +08:00
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
be69c71d4a Merge pull request 'gjson的二次处理' (#6) from feature/json into master
Reviewed-on: #6
2024-11-29 17:30:35 +08:00
7743ee14ce 按照模板格式配置数组, 数据过滤时逐层展开 2024-11-29 15:51:33 +08:00
69066c35c2 数组支持按项展开 2024-11-28 15:40:24 +08:00
33815072ea 增加多维数组的测试 2024-11-27 16:15:37 +08:00
435edb0ea3 基于gjson,递归获取全部key的路径 2024-11-27 16:07:32 +08:00
d115e1c5ba 增加gjson安全数字类型转换的hack 2024-11-27 11:26:37 +08:00
fd1f3367ed update go mod 2024-11-27 10:35:08 +08:00
fcf1a7fbd4 优化数据类型定义 2024-11-25 14:57:08 +08:00
44de8814d8 类型使用枚举值适配 2024-11-25 14:29:19 +08:00
31af48e926 update go mod 2024-11-19 14:37:17 +08:00
eba6a67875 Merge pull request '升级json数据过滤' (#5) from feature/upgrade_filter into master
Reviewed-on: #5
2024-09-25 18:40:54 +08:00
3107ca239f 增加map冲突性配置的处理 2024-09-25 18:35:40 +08:00
0e4fabcaee 升级优化 getValue 数据值获取逻辑 2024-09-25 18:03:52 +08:00
d5db651282 优化数据结构定义 2024-09-25 17:26:49 +08:00
b6a27fffd5 update json tree 2024-09-23 15:59:09 +08:00
cd52bbb02b 升级类型枚举 2024-06-08 20:17:14 +08:00
45b7aea925 update go mod 2024-06-08 19:49:32 +08:00
60c8893537 update go mod 2024-06-06 19:06:16 +08:00
00a7b0a47a update go mod 2024-03-28 15:38:22 +08:00
3b77b6fbd8 update go mod 2023-12-20 12:21:52 +08:00
024f35ac67 Merge pull request '完成数据过主干逻辑,细节健壮性有待继续完善' (#4) from feature/data_filter into master
Reviewed-on: #4
2023-09-02 22:17:13 +08:00
2230411f76 引入虚拟节点, 归一化处理list与map 2023-09-02 22:14:36 +08:00
f3baa17bd5 升级json数据过滤
升级json数据过滤
2023-09-02 21:30:41 +08:00
2634c53b79 配置提取规则脱敏 2023-09-02 12:35:03 +08:00
34913edf28 映射非数组, 数据源数组处理 2023-09-02 11:38:15 +08:00
e79ac95858 升级提取指定深度的数据最终字段作为list 2023-09-01 23:17:04 +08:00
bae9380210 update log 2023-09-01 19:18:39 +08:00
8fd5b54a0c 增加debug log 2023-09-01 18:17:20 +08:00
e6c97685e7 支持调试模式 2023-09-01 17:58:11 +08:00
f802550a49 update 2023-09-01 17:57:02 +08:00
48f3d80275 普通对象数据过滤 2023-09-01 16:26:17 +08:00
435b339604 save code 2023-08-31 21:06:35 +08:00
1cdf5449fd add test 2023-08-31 21:02:54 +08:00
81f9c552dd 去除右边相关数据 2023-08-28 12:21:24 +08:00
78b3ae43e5 解决输入数据即为数组, 结构解析错误问题 2023-08-27 00:22:42 +08:00
4bf4d84db4 update go mod 2023-08-11 15:25:18 +08:00
f87b2d08c2 update go mod 2023-08-10 17:21:01 +08:00
988acc124b 优化是否能转换json的判断 2023-08-10 17:14:21 +08:00
7004fb4808 update go mod 2023-08-09 10:38:43 +08:00
ca6ced064e 增加go json 2023-07-05 15:52:07 +08:00
70a1fe7c06 fix filter 2023-07-05 15:50:15 +08:00
4be118d7c6 update go mod 2023-06-27 17:49:24 +08:00
24 changed files with 2742 additions and 607 deletions

38
abstract/json_read.go Normal file
View File

@ -0,0 +1,38 @@
// Package abstract ...
//
// Description : abstract ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-05-06 10:42
package abstract
// IJsonRead json数据读取接口约束
type IJsonRead 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)
}

26
abstract/json_write.go Normal file
View File

@ -0,0 +1,26 @@
// Package abstract ...
//
// Description : abstract ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-05-06 10:53
package abstract
// IJsonWrite json数据写入
type IJsonWrite interface {
// Set 设置一个路径的值
Set(dataPath string, data any) error
// Delete 删除一个路径
Delete(dataPath string) error
// Result 最终结果以字符串形式返回
Result() string
// Map 最终结果以map返回
Map() (map[string]any, error)
// MapWithReceiver 外部指针接收返回值
MapWithReceiver(receiver any) error
// Array 最终结果以数组返回
Array() ([]any, error)
// ArrayWithReceiver 外部指针接收返回值
ArrayWithReceiver(receiver any) error
}

View File

@ -7,15 +7,19 @@
// Date : 2022-07-04 18:02
package filter
import (
"git.zhangdeman.cn/zhangdeman/consts"
)
// MapRule 映射规则
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:21 2022/7/4
type MapRule struct {
SourcePath string `json:"source_path"` // 原路径
MapPath string `json:"map_path"` // 映射路径
Required bool `json:"required"` // 必须存在
DataType string `json:"data_type"` // 数据类型
DefaultValue string `json:"default_value"` // 默认值, 以字符串传入, 会转换成 DataType
SourcePath string `json:"source_path"` // 原路径
TargetPath string `json:"target_path"` // 目标路径路径
Required bool `json:"required"` // 必须存在
DataType consts.DataType `json:"data_type"` // 数据类型
DefaultValue string `json:"default_value"` // 默认值, 以字符串传入, 会转换成 DataType
}

171
filter.go
View File

@ -8,17 +8,31 @@
package filter
import (
"encoding/json"
"fmt"
"git.zhangdeman.cn/zhangdeman/wrapper"
"git.zhangdeman.cn/zhangdeman/json_filter/abstract"
"git.zhangdeman.cn/zhangdeman/json_filter/implement"
"strings"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
"errors"
"git.zhangdeman.cn/zhangdeman/json_filter/gjson_hack"
"github.com/tidwall/gjson"
)
func NewFilterWithJson(sourceData string, filterRuleList []MapRule, jsonRead abstract.IJsonRead, jsonWrite abstract.IJsonWrite) *filter {
if nil == jsonRead {
jsonRead = implement.NewGjsonRead(sourceData)
}
if nil == jsonWrite {
jsonWrite = implement.NewSjsonWrite()
}
return &filter{
jsonRaad: jsonRead,
jsonWrite: jsonWrite,
sourceData: sourceData,
filterRuleList: filterRuleList,
}
}
// NewFilter 过滤器实例
//
// Author : go_developer@163.com<白茶清欢>
@ -27,7 +41,8 @@ import (
func NewFilter(sourceData string, filterRuleList []MapRule) *filter {
return &filter{
sourceData: sourceData,
formatResult: "{}",
jsonRaad: implement.NewGjsonRead(sourceData),
jsonWrite: implement.NewSjsonWrite(),
filterRuleList: filterRuleList,
}
}
@ -38,8 +53,9 @@ func NewFilter(sourceData string, filterRuleList []MapRule) *filter {
//
// Date : 11:58 2022/7/4
type filter struct {
jsonRaad abstract.IJsonRead
jsonWrite abstract.IJsonWrite
sourceData string
formatResult string
filterRuleList []MapRule
}
@ -50,23 +66,22 @@ type filter struct {
// Date : 11:59 2022/7/4
func (f *filter) Deal() error {
var (
err error
formatVal interface{}
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 f.formatResult, err = sjson.Set(f.formatResult, rule.MapPath, formatVal); nil != err {
if err = f.setResult(rule); nil != err {
return err
}
}
@ -79,7 +94,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 处理数组(最复杂的场景)
@ -88,33 +103,48 @@ 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.MapPath, ".[]"), "[]")
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
)
if formatVal, err = f.jsonRaad.Value(rule.SourcePath, rule.DataType.String(), rule.DefaultValue); nil != err {
return fmt.Errorf("%s = %v can not convert to %s : %s", rule.SourcePath, gjson.Get(f.sourceData, rule.SourcePath).String(), rule.DataType, err.Error())
}
if err = f.jsonWrite.Set(rule.TargetPath, formatVal); nil != err {
return err
}
return nil
}
@ -143,7 +173,7 @@ func (f *filter) getAllFinalData(res *[]string, resultList []gjson.Result, pathA
//
// Date : 21:18 2022/12/31
func (f *filter) String() string {
return f.formatResult
return f.jsonWrite.Result()
}
// Byte 获取格式化之后的字节数组
@ -160,73 +190,10 @@ func (f *filter) Byte() []byte {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:20 2022/12/31
func (f *filter) Parse(receiver interface{}) error {
func (f *filter) Parse(receiver any) error {
if nil == receiver {
return errors.New("receiver is nil")
}
return json.Unmarshal(f.Byte(), receiver)
}
// getValue 获取值
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:25 2022/7/4
func (f *filter) getValue(dataType string, sourceValue gjson.Result, defaultValue string) (interface{}, error) {
sourceValueStr := defaultValue
if sourceValue.Exists() {
str := sourceValue.String()
if len(str) > 0 {
sourceValueStr = str
}
}
strVal := wrapper.String(sourceValueStr)
switch dataType {
case wrapper.DataTypeInt8:
return strVal.ToInt8()
case wrapper.DataTypeInt16:
return strVal.ToInt16()
case wrapper.DataTypeInt32:
return strVal.ToInt32()
case wrapper.DataTypeInt64:
return strVal.ToInt64()
case wrapper.DataTypeInt:
return strVal.ToInt()
case wrapper.DataTypeUint8:
return strVal.ToUint8()
case wrapper.DataTypeUint16:
return strVal.ToUint16()
case wrapper.DataTypeUint32:
return strVal.ToUint32()
case wrapper.DataTypeUint64:
return strVal.ToUint64()
case wrapper.DataTypeUint:
return strVal.ToUint()
case wrapper.DataTypeBool:
return strVal.ToBool()
case wrapper.DataTypeFloat32:
return strVal.ToFloat64()
case wrapper.DataTypeFloat64:
fallthrough
case wrapper.DataTypeFloat:
fallthrough
case wrapper.DataTypeDouble:
return strVal.ToFloat64()
case wrapper.DataTypeNumber:
return strVal.ToNumber()
case wrapper.DataTypeString:
return sourceValueStr, nil
case wrapper.DataTypeAny:
return sourceValue.Value(), nil
case wrapper.DataTypeAnySlice:
// 任意类型的list
return strVal.ToAnySlice()
case wrapper.DataTypeObject:
// object
return strVal.ToObject()
default:
return nil, errors.New(dataType + " is not support!")
}
return f.jsonWrite.MapWithReceiver(receiver)
// return json.Unmarshal(f.Byte(), receiver)
}

View File

@ -8,6 +8,7 @@
package filter
import (
"encoding/json"
"fmt"
"testing"
@ -37,35 +38,35 @@ func TestNewFilter(t *testing.T) {
filterRuleList := []MapRule{
{
SourcePath: "base.name",
MapPath: "user_name",
TargetPath: "user_name",
Required: true,
DataType: "string",
DefaultValue: "",
},
{
SourcePath: "base.age",
MapPath: "user_age",
TargetPath: "user_age",
Required: true,
DataType: "int",
DefaultValue: "",
},
{
SourcePath: "base.height",
MapPath: "user_height",
TargetPath: "user_height",
Required: true,
DataType: "string",
DefaultValue: "",
},
{
SourcePath: "company.name",
MapPath: "company_name",
TargetPath: "company_name",
Required: true,
DataType: "string",
DefaultValue: "",
},
{
SourcePath: "company.start",
MapPath: "company_start",
TargetPath: "company_start",
Required: true,
DataType: "string",
DefaultValue: "",
@ -105,7 +106,7 @@ func TestNewFilterForArrayOne(t *testing.T) {
filterRuleList := []MapRule{
{
SourcePath: "[].name",
MapPath: "user_name.[]",
TargetPath: "user_name.[]",
Required: true,
DataType: "string",
DefaultValue: "",
@ -145,7 +146,7 @@ func TestNewFilterForArrayTwo(t *testing.T) {
filterRuleList := []MapRule{
{
SourcePath: "user_list.[].name",
MapPath: "user.name_list.[]",
TargetPath: "user.name_list.[]",
Required: true,
DataType: "string",
DefaultValue: "",
@ -158,3 +159,13 @@ func TestNewFilterForArrayTwo(t *testing.T) {
fmt.Println(f.String())
})
}
func TestAntMap(t *testing.T) {
testMap := map[any]any{
"name": "zhangde",
1: 1,
1.234: 2.345,
}
byteData, err := json.Marshal(testMap)
fmt.Println(string(byteData), err)
}

3
gjson_hack/README.md Normal file
View File

@ -0,0 +1,3 @@
# 对gjson库一些已知问题的pack
- 大数字 res.Int() 缺失精度

50
gjson_hack/define.go Normal file
View File

@ -0,0 +1,50 @@
// Package gjson_hack ...
//
// Description : gjson_hack ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-11-27 11:59
package gjson_hack
import "github.com/tidwall/gjson"
// PathOption 路径操作的选项
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:01 2024/11/27
type PathOption struct {
UnfoldArray bool `json:"unfold_array"` // 展开数组
MaxDeep int `json:"max_deep"` // 迭代最大深度, 默认 0 不限制
OnlyFinalPath bool `json:"only_final_path"` // 仅展示最终路径
}
// PathResult ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:00 2024/11/27
type PathResult struct {
List []string // 全部路径列表
ValueTable map[string]gjson.Result // 路径对应的值
}
const (
ArrayIdxTpl = "[]"
)
// ExpendArrayResult 展开数组的结果
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:46 2024/11/29
type ExpendArrayResult struct {
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
var (
ErrDataPathNotFound = "data_path_not_found"
ErrDataIsNotObject = "data_is_not_object"
ErrDataIsNotArray = "data_is_not_an_array"
ErrDataTypeIsNotSupport = "data_type_is_not_support"
)

363
gjson_hack/path.go Normal file
View File

@ -0,0 +1,363 @@
// 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)
}

201
gjson_hack/path_test.go Normal file
View File

@ -0,0 +1,201 @@
// Package gjson_hack ...
//
// Description : gjson_hack ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-11-27 15:52
package gjson_hack
import (
"encoding/json"
"fmt"
"testing"
"github.com/tidwall/gjson"
)
func TestPath(t *testing.T) {
mapData := map[string]any{
"person_list": []map[string]any{
{"name": "zhang", "age": 10},
{"name": "li", "age": 20},
{"name": "wang", "age": 30},
},
"company_info": map[string]any{
"address": "Beijing",
"email": "xxx@xxx.com",
},
"sex": "man",
}
byteData, _ := json.Marshal(mapData)
pathRes, err := Path(string(byteData), nil)
if nil != err {
fmt.Println(err.Error())
} else {
for _, item := range pathRes.List {
fmt.Println(item)
}
}
}
func TestPathOnlyFinallyPath(t *testing.T) {
mapData := map[string]any{
"person_list": []map[string]any{
{"name": "zhang", "age": 10},
{"name": "li", "age": 20},
{"name": "wang", "age": 30},
},
"company_info": map[string]any{
"address": "Beijing",
"email": "xxx@xxx.com",
},
"sex": "man",
"user_list": [][]map[string]any{
[]map[string]any{
{"name": "zhang", "age": 10},
{"name": "li", "age": 20},
{"name": "wang", "age": 30},
},
[]map[string]any{
{"name": "zhang", "age": 20},
{"name": "li", "age": 20},
{"name": "wang", "age": 30},
},
[]map[string]any{
{"name": "zhang", "age": 30},
{"name": "li", "age": 20},
{"name": "wang", "age": 30},
},
},
}
byteData, _ := json.Marshal(mapData)
fmt.Println(gjson.GetBytes(byteData, "user_list.#.#.age"))
fmt.Println(gjson.GetBytes(byteData, "user_list.0.0.age"))
pathRes, err := Path(string(byteData), &PathOption{
UnfoldArray: false,
MaxDeep: 0,
OnlyFinalPath: true,
})
if nil != err {
fmt.Println(err.Error())
} else {
for _, item := range pathRes.List {
fmt.Println(item)
}
}
}
func TestPathOnlyFinallyPathWithUnfoldArray(t *testing.T) {
mapData := map[string]any{
"person_list": []map[string]any{
{"name": "zhang", "age": 10},
{"name": "li", "age": 20},
{"name": "wang", "age": 30},
},
"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, "e.f": "g"},
{"name": "li", "age": 20, "e.f": "g"},
{"name": "wang", "age": 30, "e.f": "g"},
},
[]map[string]any{
{"name": "zhang", "age": 10},
{"name": "li", "age": 20},
{"name": "wang", "age": 30},
},
[]map[string]any{
{"name": "zhang", "age": 10},
{"name": "li", "age": 20},
{"name": "wang", "age": 30},
},
},
}
byteData, _ := json.Marshal(mapData)
pathRes, err := Path(string(byteData), &PathOption{
UnfoldArray: true,
MaxDeep: 0,
OnlyFinalPath: false,
})
if nil != err {
fmt.Println(err.Error())
} else {
for _, item := range pathRes.List {
fmt.Println(item)
}
}
}
func TestExpandArrayPath(t *testing.T) {
mapData := map[string]any{
"person_list": []map[string]any{
{"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",
"level.a.b": "deep level",
},
"sex": "man",
"user_list": [][]map[string]any{
[]map[string]any{
{"name": "zhang", "age": 10},
{"name": "li", "age": 20},
{"name": "wang", "age": 30},
},
[]map[string]any{
{"name": "zhang", "age": 10},
{"name": "li", "age": 20},
{"name": "wang", "age": 30},
},
[]map[string]any{
{"name": "zhang", "age": 10},
{"name": "li", "age": 20},
{"name": "wang", "age": 30},
},
},
}
byteData, _ := json.Marshal(mapData)
jsonStr := string(byteData)
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())
}

469
gjson_hack/precision.go Normal file
View File

@ -0,0 +1,469 @@
// Package gjson_hack 精确地类型转换
//
// Description : gjson_hack ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-11-27 10:47
package gjson_hack
import (
"errors"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/exception"
"git.zhangdeman.cn/zhangdeman/serialize"
"git.zhangdeman.cn/zhangdeman/util"
"git.zhangdeman.cn/zhangdeman/wrapper"
"github.com/tidwall/gjson"
"strings"
)
// Number 结果转换为数字(int64 / uint64 / float64)
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:57 2024/11/27
func Number(gjsonResult gjson.Result, numberReceiver any) error {
if !gjsonResult.Exists() {
return errors.New("gjson result not found")
}
if gjsonResult.Type != gjson.Number {
return errors.New(gjsonResult.String() + " : value not a number")
}
if err := util.ConvertAssign(numberReceiver, gjsonResult.String()); nil != err {
return errors.New(gjsonResult.String() + " : convert to num fail -> " + err.Error())
}
return nil
}
// Int 转int
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:19 2024/11/27
func Int[T int8 | int16 | int32 | int64 | int](gjsonResult gjson.Result) (T, error) {
var intResult T
if err := Number(gjsonResult, &intResult); nil != err {
return 0, err
}
return intResult, nil
}
// Uint 转为uint64
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:21 2024/11/27
func Uint[T uint8 | uint16 | uint32 | uint64 | uint](gjsonResult gjson.Result) (T, error) {
var uintResult T
if err := Number(gjsonResult, &uintResult); nil != err {
return 0, err
}
return uintResult, nil
}
// Float 转为float64
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:24 2024/11/27
func Float[T float32 | float64](gjsonResult gjson.Result) (T, error) {
var floatResult T
if err := Number(gjsonResult, &floatResult); nil != err {
return 0, err
}
return floatResult, 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.Exists() {
return nil, exception.New(ErrDataPathNotFound, nil, "data path not found")
}
if !sourceValue.IsObject() {
return nil, exception.New(ErrDataIsNotObject, nil)
}
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.Type == gjson.Number {
// 说明是数字
if strings.Contains(sourceValue.String(), ".") {
// 小数
var res float64
if err := Number(sourceValue, &res); nil != err {
return nil, err
}
return res, nil
} else {
// 整数
if strings.HasPrefix(sourceValue.String(), "-") {
// 负数
var res int64
if err := Number(sourceValue, &res); nil != err {
return nil, err
}
return res, nil
} else {
// 正数
var res uint64
if err := Number(sourceValue, &res); 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(), "*"))
// 没指定数据类型或者数据类型是any
if dataType == consts.DataTypeAny || dataType == "" {
if sourceValue.IsObject() {
dataType = consts.DataTypeMapAnyAny
} else if sourceValue.IsArray() {
dataType = consts.DataTypeSliceAny
} else if sourceValue.IsBool() {
dataType = consts.DataTypeBool
} else {
if sourceValue.Type == gjson.Number {
dataType = consts.DataTypeFloat64
} else if sourceValue.Type == gjson.String {
dataType = consts.DataTypeString
} else {
dataType = consts.DataTypeAny
}
}
}
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 !gjsonResult.IsArray() {
return nil, exception.New(ErrDataIsNotArray, nil)
}
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 !gjsonResult.IsArray() {
return nil, exception.New(ErrDataIsNotArray, nil)
}
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, exception.New(ErrDataIsNotArray, nil)
}
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[int](sourceValue)
case consts.DataTypeInt8:
return Int[int8](sourceValue)
case consts.DataTypeInt16:
return Int[int16](sourceValue)
case consts.DataTypeInt32:
return Int[int32](sourceValue)
case consts.DataTypeInt64:
return Int[int64](sourceValue)
case consts.DataTypeUint:
return Uint[uint](sourceValue)
case consts.DataTypeUint8:
return Uint[uint8](sourceValue)
case consts.DataTypeUint16:
return Uint[uint16](sourceValue)
case consts.DataTypeUint32:
return Uint[uint32](sourceValue)
case consts.DataTypeUint64:
return Uint[uint64](sourceValue)
case consts.DataTypeFloat64:
return Float[float64](sourceValue)
case consts.DataTypeFloat32:
return Float[float32](sourceValue)
case consts.DataTypeBool:
boolVal := strVal.ToBool()
return boolVal.Value, boolVal.Err
case consts.DataTypeString:
return sourceValue.String(), nil
case consts.DataTypeAny:
// 经过getRealDataType类型处理后依旧是any,不考虑精度问题
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, exception.New(ErrDataTypeIsNotSupport, map[string]any{"data_type": dataType.String()})
}
}

32
go.mod
View File

@ -1,31 +1,35 @@
module git.zhangdeman.cn/zhangdeman/json_filter
go 1.18
go 1.23.0
toolchain go1.24.3
require (
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20220627070212-c590a0a1c216
git.zhangdeman.cn/zhangdeman/util v0.0.0-20230505025924-96532aff0019
github.com/Jeffail/gabs v1.4.0
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250425024726-cc17224cb995
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20250504055908-8d68e6106ea9
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250321102712-1cbfbe959740
github.com/pkg/errors v0.9.1
github.com/smartystreets/goconvey v1.7.2
github.com/tidwall/gjson v1.14.4
github.com/smartystreets/goconvey v1.8.1
github.com/tidwall/gjson v1.18.0
github.com/tidwall/sjson v1.2.5
gopkg.in/yaml.v3 v3.0.1
)
require (
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20230505080742-fa2f27724d76 // indirect
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250510123912-a0d52fc093ab // indirect
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mssola/user_agent v0.6.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/smartystreets/assertions v1.2.0 // indirect
github.com/mozillazg/go-pinyin v0.20.0 // indirect
github.com/sbabiv/xml2map v1.2.1 // indirect
github.com/smarty/assertions v1.15.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

79
go.sum
View File

@ -1,57 +1,56 @@
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20220627070212-c590a0a1c216 h1:5M6YsHmbTqLDGkqKykeoxrrd5WsAme6I+akn9sgKUZk=
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20220627070212-c590a0a1c216/go.mod h1:8Jh+q/L0pWAKQy67m8AKIs0WGGrVKypoSZZbBm4nDVc=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20220626081130-cbac0b676fb8/go.mod h1:G2/OKMbEn89d+YUXQtv9Nlh0LGg14pOqDnbOgBTTRXY=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20230211164227-256094968151 h1:j537bRLQL1FlkdXTIaT9Ecjx5eogkPsGiTOWIEFQlc8=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20230211164227-256094968151/go.mod h1:SyRTkOz6gxUVn3S/Qtkf+rhKV0I1ym8lwsT8YjggYFs=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20230330065032-faba0a5a9ea1 h1:u2FdNfcGvRmlKpuPBk2qvYenkZjinHY2PLu5Wmhka8A=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20230330065032-faba0a5a9ea1/go.mod h1:SyRTkOz6gxUVn3S/Qtkf+rhKV0I1ym8lwsT8YjggYFs=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20230330082619-662152cb682d h1:kMQZmkYBceHM3O7wiCelSADjTyOF3EBxXTX8fgZA+6c=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20230330082619-662152cb682d/go.mod h1:qeVsrMae8ljqzcsmI+lWPU/4Rdjb9cOt4oaDUNEf1Ck=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20230505025924-96532aff0019 h1:eJ/9rEj2iI8P9I1DfCmMUvsV+n2EiAWCXnI9yVVDHO0=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20230505025924-96532aff0019/go.mod h1:z2bP5LIwRVpWSQV0/a3WIFaoarJUP8kA/0Clv0bP+8I=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20230505064614-5e785171ed67 h1:DH9K3fNddpFxRGLkcLP5MHsAQVinpWpmGzbVBf8yrKM=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20230505064614-5e785171ed67/go.mod h1:2jc48WuVoHxZjkvlBewzp+ey8khP1K4OOcibVD1yL2k=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20230505070245-b182b21e039b h1:ZwAA10/+v3FFAq5/EzjXdXDodmKppXb5gBkCcgaYVBo=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20230505070245-b182b21e039b/go.mod h1:2jc48WuVoHxZjkvlBewzp+ey8khP1K4OOcibVD1yL2k=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20230505080742-fa2f27724d76 h1:IEixZNZY3/nqb5v9PzzgSeXLPp0U7wRaxrM+ZaSh+j0=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20230505080742-fa2f27724d76/go.mod h1:2jc48WuVoHxZjkvlBewzp+ey8khP1K4OOcibVD1yL2k=
github.com/Jeffail/gabs v1.4.0 h1://5fYRRTq1edjfIrQGvdkcd22pkYUrHZ5YC/H2GJVAo=
github.com/Jeffail/gabs v1.4.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250321102241-d6e86b64f7ca h1:uxjzbY5fDozjyK6jkoQtuQouVTcVfXjbe3chARYSjRM=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250321102241-d6e86b64f7ca/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250328040304-7e4a6f9f148c h1:cl3gQGXQpJ8ugDs0C/hQLfcvF4lGBm5BeABLvROFDoM=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250328040304-7e4a6f9f148c/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250425024726-cc17224cb995 h1:LmPRAf0AsxRVFPibdpZR89ajlsz8hof2IvMMyTqiEq4=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250425024726-cc17224cb995/go.mod h1:5p8CEKGBxi7qPtTXDI3HDmqKAfIm5i/aBWdrbkbdNjc=
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda h1:bMD6r9gjRy7cO+T4zRQVYAesgIblBdTnhzT1vN5wjvI=
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda/go.mod h1:dT0rmHcJ9Z9IqWeMIt7YzR88nKkNV2V3dfG0j9Q6lK0=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250510123912-a0d52fc093ab h1:O0XaAKKb8qrjcjewonmKfnRsMFoCfJF+tUv6RfhRe94=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250510123912-a0d52fc093ab/go.mod h1:Voc8J4ordx7nuMWpgACXXZULQy7ZIuBzcEIoS8VnDIw=
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 h1:gUDlQMuJ4xNfP2Abl1Msmpa3fASLWYkNlqDFF/6GN0Y=
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0/go.mod h1:VHb9qmhaPDAQDcS6vUiDCamYjZ4R5lD1XtVsh55KsMI=
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd h1:q7GG14qgXKB4MEXQFOe7/UYebsqMfPaSX80TcPdOosI=
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd/go.mod h1:+D6uPSljwHywjVY5WSBY4TRVMj26TN5f5cFGEYMldjs=
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20250504055908-8d68e6106ea9 h1:/GLQaFoLb+ciHOtAS2BIyPNnf4O5ME3AC5PUaJY9kfs=
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20250504055908-8d68e6106ea9/go.mod h1:ABJ655C5QenQNOzf7LjCe4sSB52CXvaWLX2Zg4uwDJY=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e h1:Q973S6CcWr1ICZhFI1STFOJ+KUImCl2BaIXm6YppBqI=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250321102712-1cbfbe959740 h1:zPUoylfJTbc0EcxW+NEzOTBmoeFZ2I/rLFBnEzxb4Wk=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250321102712-1cbfbe959740/go.mod h1:1ct92dbVc49pmXusA/iGfcQUJzcYmJ+cjAhgc3sDv1I=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mssola/user_agent v0.6.0 h1:uwPR4rtWlCHRFyyP9u2KOV0u8iQXmS7Z7feTrstQwk4=
github.com/mssola/user_agent v0.6.0/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
github.com/mozillazg/go-pinyin v0.20.0 h1:BtR3DsxpApHfKReaPO1fCqF4pThRwH9uwvXzm+GnMFQ=
github.com/mozillazg/go-pinyin v0.20.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
github.com/sbabiv/xml2map v1.2.1 h1:1lT7t0hhUvXZCkdxqtq4n8/ZCnwLWGq4rDuDv5XOoFE=
github.com/sbabiv/xml2map v1.2.1/go.mod h1:2TPoAfcaM7+Sd4iriPvzyntb2mx7GY+kkQpB/GQa/eo=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@ -59,13 +58,7 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

142
implement/gjson_read.go Normal file
View File

@ -0,0 +1,142 @@
// Package implement ...
//
// Description : implement ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-05-06 11:00
package implement
import (
"errors"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/json_filter/abstract"
"git.zhangdeman.cn/zhangdeman/json_filter/gjson_hack"
"git.zhangdeman.cn/zhangdeman/serialize"
"github.com/tidwall/gjson"
"strings"
)
// NewGjsonRead ...
func NewGjsonRead(sourceData string) abstract.IJsonRead {
return &gjsonRead{
sourceData: sourceData,
gjsonResult: gjson.Parse(sourceData),
}
}
type gjsonRead struct {
sourceData string
gjsonResult gjson.Result
}
func (g *gjsonRead) Exist(dataPath string) bool {
return g.gjsonResult.Get(dataPath).Exists()
}
func (g *gjsonRead) IsNil(dataPath string) bool {
return g.gjsonResult.Get(dataPath).Value() == nil
}
func (g *gjsonRead) Type(dataPath string) string {
pathRes := g.gjsonResult.Get(dataPath)
if pathRes.IsObject() { // map
return consts.DataTypeMapStrAny.String()
}
if pathRes.IsArray() { // slice
return consts.DataTypeSliceAny.String()
}
if pathRes.IsBool() { // bool
return consts.DataTypeBool.String()
}
dataType := pathRes.Type
switch dataType {
case gjson.String:
return consts.DataTypeString.String()
case gjson.Number:
valStr := pathRes.String()
if strings.Contains(valStr, ".") {
return consts.DataTypeFloat64.String()
}
if strings.HasPrefix(valStr, "-") {
return consts.DataTypeInt64.String()
}
return consts.DataTypeUint64.String()
case gjson.Null:
return consts.DataTypeAny.String()
case gjson.True, gjson.False:
return consts.DataTypeBool.String()
}
return consts.DataTypeAny.String() // any类型兜底
}
func (g *gjsonRead) Int(dataPath string) (int64, error) {
return gjson_hack.Int[int64](g.gjsonResult.Get(dataPath))
}
func (g *gjsonRead) Uint(dataPath string) (uint64, error) {
return gjson_hack.Uint[uint64](g.gjsonResult.Get(dataPath))
}
func (g *gjsonRead) Float(dataPath string) (float64, error) {
return gjson_hack.Float[float64](g.gjsonResult.Get(dataPath))
}
func (g *gjsonRead) Bool(dataPath string) (bool, error) {
res := g.gjsonResult.Get(dataPath)
if !res.IsBool() {
return false, errors.New(dataPath + ": is not a bool")
}
return res.Bool(), nil
}
func (g *gjsonRead) String(dataPath string) (string, error) {
if !g.Exist(dataPath) {
return "", errors.New(dataPath + ": not found")
}
return g.gjsonResult.Get(dataPath).String(), nil
}
func (g *gjsonRead) Map(dataPath string) (map[string]any, error) {
var (
err error
dataMap map[string]any
)
if err = g.MapWithReceiver(dataPath, &dataMap); nil != err {
return map[string]any{}, err
}
return dataMap, nil
}
func (g *gjsonRead) MapWithReceiver(dataPath string, receiver any) error {
strVal, err := g.String(dataPath)
if nil != err {
return err
}
return serialize.JSON.UnmarshalWithNumber([]byte(strVal), receiver)
}
func (g *gjsonRead) Array(dataPath string) ([]any, error) {
var (
err error
dataArr []any
)
if err = g.ArrayWithReceiver(dataPath, &dataArr); nil != err {
return []any{}, err
}
return dataArr, nil
}
func (g *gjsonRead) ArrayWithReceiver(dataPath string, receiver any) error {
strVal, err := g.String(dataPath)
if nil != err {
return err
}
return serialize.JSON.UnmarshalWithNumber([]byte(strVal), receiver)
}
func (g *gjsonRead) Value(dataPath string, dataType string, defaultValue any) (any, error) {
if len(dataType) == 0 {
dataType = g.Type(dataPath)
}
return gjson_hack.Value(consts.DataType(dataType), g.gjsonResult.Get(dataPath), defaultValue)
}

128
implement/sjson_write.go Normal file
View File

@ -0,0 +1,128 @@
// Package implement ...
//
// Description : implement ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-05-06 11:53
package implement
import (
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/json_filter/abstract"
"git.zhangdeman.cn/zhangdeman/serialize"
"github.com/tidwall/sjson"
"reflect"
"sync"
)
func NewSjsonWrite() abstract.IJsonWrite {
return &SjsonWrite{
res: "",
l: &sync.RWMutex{},
}
}
type SjsonWrite struct {
res string
l *sync.RWMutex
}
func (s *SjsonWrite) Delete(dataPath string) error {
var (
err error
)
s.l.Lock()
defer s.l.Unlock()
if s.res, err = sjson.Delete(s.res, dataPath); nil != err {
return err
}
return nil
}
func (s *SjsonWrite) Set(dataPath string, data any) error {
var (
err error
)
s.l.Lock()
defer s.l.Unlock()
existResRead := NewGjsonRead(s.res)
if !existResRead.Exist(dataPath) || data == nil {
// 路径不存在
if s.res, err = sjson.Set(s.res, dataPath, data); nil != err {
return err
}
return nil
}
// 路径已存在, 判断是否为map
if existResRead.Type(dataPath) != consts.DataTypeMapStrAny.String() {
if s.res, err = sjson.Set(s.res, dataPath, data); nil != err {
return err
}
return nil
}
// 判断data是否为map
dataType := reflect.TypeOf(data)
if dataType.Kind() == reflect.Ptr {
dataType = dataType.Elem()
}
if dataType.Kind() != reflect.Struct && dataType.Kind() != reflect.Map {
// 既不是map, 也不是struct
if s.res, err = sjson.Set(s.res, dataPath, data); nil != err {
return err
}
return nil
}
// 路径已存在, 已存在数据为map, 且要设置的数据也为map, 进行数据合并
mergeDataMap := map[string]any{}
existMapVal := map[string]any{}
if existMapVal, err = existResRead.Map(dataPath); nil != err {
return err
}
// 合并输入数据
if err = serialize.JSON.MergeDataForReceiver(&mergeDataMap, existMapVal, data); nil != err {
return err
}
if s.res, err = sjson.Set(s.res, dataPath, mergeDataMap); nil != err {
return err
}
return nil
}
func (s *SjsonWrite) Result() string {
s.l.RLock()
defer s.l.RUnlock()
return s.res
}
func (s *SjsonWrite) Map() (map[string]any, error) {
var (
mapRes map[string]any
err error
)
if err = s.MapWithReceiver(&mapRes); nil != err {
return nil, err
}
return mapRes, nil
}
func (s *SjsonWrite) MapWithReceiver(receiver any) error {
return serialize.JSON.UnmarshalWithNumberForString(s.Result(), receiver)
}
func (s *SjsonWrite) Array() ([]any, error) {
var (
arrRes []any
err error
)
if err = s.ArrayWithReceiver(&arrRes); nil != err {
return nil, err
}
return arrRes, nil
}
func (s *SjsonWrite) ArrayWithReceiver(receiver any) error {
return serialize.JSON.UnmarshalWithNumberForString(s.Result(), receiver)
}

66
sjson_hack/set.go Normal file
View File

@ -0,0 +1,66 @@
// Package sjson_hack ...
//
// Description : sjson_hack ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-12-03 11:36
package sjson_hack
import (
"errors"
"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) {
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)
}

View File

@ -10,14 +10,10 @@ package json_tool
import (
"encoding/json"
"fmt"
"reflect"
"github.com/tidwall/gjson"
"strings"
"github.com/tidwall/gjson"
"github.com/pkg/errors"
"git.zhangdeman.cn/zhangdeman/util"
)
// MapDataRule 数据映射结果
@ -109,37 +105,7 @@ func (f *Filter) SetValue(result *DynamicJSON, path string, val interface{}, isS
//
// Date : 10:46 下午 2021/3/14
func (f *Filter) isLegalData() bool {
val := reflect.ValueOf(f.data)
switch val.Kind() {
case reflect.Slice:
// slice 情况下,对字节数组进行特殊判断
var (
byteData []byte
ok bool
err error
)
if byteData, ok = f.data.([]byte); ok {
// 字节数组转map或者slice
if err = json.Unmarshal(byteData, &f.data); nil != err {
return false
}
return true
}
return true
case reflect.Map:
return true
case reflect.Struct:
// 结构体转为字符串处理
fallthrough
case reflect.Ptr:
// 指针
var err error
if f.data, err = util.Struct.ToMap(f.data); nil != err {
return false
}
return true
default:
return false
}
byteData, _ := json.Marshal(f.data)
dataRes := gjson.Parse(string(byteData))
return dataRes.IsObject() || dataRes.IsArray()
}

View File

@ -8,19 +8,32 @@
package json_tool
import (
"encoding/json"
"fmt"
"reflect"
"io"
"log"
"os"
"strings"
"git.zhangdeman.cn/zhangdeman/util"
"github.com/pkg/errors"
"github.com/tidwall/gjson"
"github.com/Jeffail/gabs"
"github.com/tidwall/sjson"
)
const (
// virtualRoot 虚拟根节点
virtualRoot = "__VIRTUAL_ROOT__"
)
// FilterOption 过滤选项
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:59 2023/9/1
type FilterOption struct {
DebugModel bool // 调试模式
LogInstance io.Writer // 日志实例
}
// FilterDataRule 参数过滤规则
//
// Author : go_developer@163.com<白茶清欢>
@ -38,12 +51,45 @@ type FilterDataRule struct {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/1/22 9:50 PM
func NewDataFilter(source string, filterRule []*FilterDataRule) *DataFilter {
return &DataFilter{
source: source,
filterRule: filterRule,
hasDealDiffPath: make(map[string]string),
func NewDataFilter(source string, filterRule []*FilterDataRule, filterOption *FilterOption) *DataFilter {
source = fmt.Sprintf(`{"%v":%v}`, virtualRoot, source)
if nil == filterOption {
filterOption = &FilterOption{}
}
if filterOption.DebugModel && nil == filterOption.LogInstance {
filterOption.LogInstance = os.Stdout
log.SetOutput(filterOption.LogInstance)
}
df := &DataFilter{
source: source,
filterRule: make([]*FilterDataRule, 0),
rewriteResult: "{}",
filterOption: filterOption,
}
// 去除末尾的 .[]
for _, item := range filterRule {
item.MapKey = virtualRoot + "." + strings.TrimRight(item.MapKey, ".[]")
item.SourceKey = virtualRoot + "." + strings.TrimRight(item.SourceKey, ".[]")
mapIsArr := df.isArrPath(item.MapKey)
if !mapIsArr {
df.filterRule = append(df.filterRule, item)
continue
}
if len(df.getArrPathList(item.SourceKey)) < len(df.getArrPathList(item.MapKey)) {
panic("map result deep more than source data deep")
}
formatRes := make([]*FilterDataRule, 0)
r, _ := df.unfoldSameDeepArr(item.SourceKey, item.MapKey, "", "")
for _, itemUnfoldResult := range r {
itemMapArr := strings.Split(itemUnfoldResult.MapKey, ".")
if len(itemMapArr) != len(strings.Split(item.MapKey, ".")) {
continue
}
formatRes = append(formatRes, itemUnfoldResult)
}
df.filterRule = append(df.filterRule, formatRes...)
}
return df
}
// DataFilter 数据过滤
@ -52,10 +98,10 @@ func NewDataFilter(source string, filterRule []*FilterDataRule) *DataFilter {
//
// Date : 2022/1/22 9:20 PM
type DataFilter struct {
source string
filterRule []*FilterDataRule
itemKeyToSlice bool
hasDealDiffPath map[string]string
source string
filterRule []*FilterDataRule
rewriteResult string // json数据重写结果
filterOption *FilterOption // 过滤选项
}
// Filter 数据过滤
@ -64,231 +110,224 @@ type DataFilter struct {
//
// Date : 2022/1/22 9:36 PM
func (df *DataFilter) Filter() (string, error) {
df.logPrint(logLevelDebug, "source_data => ", df.source)
var (
jsonObject *gabs.Container
err error
err error
)
// 格式化映射规则
df.formatRule()
// 记录 obj => slice 的数据类型
obg2slice := make(map[string]string)
// 创建数据的根结点
jsonObject = gabs.New()
for _, item := range df.filterRule {
// 数据源路径不识数组, 多个key写入到同一个map key, 并且map key 不是以[]结尾, 自动格式化
// 目标位置, 是一个数组
if df.pathIsArrayValue(item.MapKey) {
realMapKey := strings.Trim(item.MapKey, ".[]")
if exist := jsonObject.Exists(realMapKey); !exist {
if _, err = jsonObject.ArrayP(realMapKey); nil != err {
return "", err
}
}
valueResult := gjson.Get(df.source, item.SourceKey)
dataType := df.getValueType(valueResult)
if _, exist := obg2slice[realMapKey]; !exist {
obg2slice[realMapKey] = dataType
}
if dataType != obg2slice[realMapKey] {
return "", errors.New(realMapKey + " 预期写入的字段数据类型不一致")
}
sourcePathArr := strings.Split(item.SourceKey, ".[].")
mapPathArr := strings.Split(realMapKey, ".[].")
result := gabs.New()
_, _ = result.ArrayP(mapPathArr[0])
df.SetArrayData("{\""+sourcePathArr[0]+"\":"+gjson.Get(df.source, sourcePathArr[0]).String()+"}", result, sourcePathArr, mapPathArr)
if err = jsonObject.ArrayAppend(valueResult.Value(), realMapKey); nil != err {
for _, itemRule := range df.filterRule {
sourceIsArr := df.isArrPath(itemRule.SourceKey)
mapIsArr := df.isArrPath(itemRule.MapKey)
if !sourceIsArr && !mapIsArr {
// 输入输出均不是数组, 最简单的场景
if err = df.setKV(itemRule); nil != err {
return "", err
}
continue
}
sourceSearchResult := gjson.Get(df.source, item.SourceKey)
if !sourceSearchResult.Exists() {
if item.WithDefault {
if _, err = jsonObject.SetP(item.DefaultValue, item.MapKey); nil != err {
return "", err
}
sourcePathArr := df.getArrPathList(itemRule.SourceKey)
mapPathArr := df.getArrPathList(itemRule.MapKey)
if len(mapPathArr) > len(sourcePathArr) {
df.logPrint(logLevelFatal, "映射的层级深度大于数据源深度", "source_path => "+itemRule.SourceKey, "map_path => "+itemRule.MapKey)
return "", fmt.Errorf("映射的层级深度大于数据源深度, source_path => %v map_path => %v", itemRule.SourceKey, itemRule.MapKey)
}
// 映射至非数组字段
if !mapIsArr {
if err = df.setValue(itemRule.MapKey, df.getDataAsSlice(df.source, df.getArrPathList(itemRule.SourceKey))); nil != err {
df.logPrint(logLevelFatal, "映射非数组, 数据源为数组, 设置失败", "source_path => "+itemRule.SourceKey, "map_path => "+itemRule.MapKey, " err => "+err.Error())
return "", fmt.Errorf("映射的层级深度大于数据源深度, source_path => %v map_path => %v", itemRule.SourceKey, itemRule.MapKey)
}
continue
}
if _, err = jsonObject.SetP(sourceSearchResult.Value(), item.MapKey); nil != err {
return "", err
if len(mapPathArr) == len(sourcePathArr) {
// 数组深度一致
continue
}
}
return jsonObject.String(), nil
df.logPrint(logLevelDebug, "过滤结果", df.rewriteResult)
return gjson.Get(df.rewriteResult, virtualRoot).String(), nil
}
// UserItemToSlice 支持多个独立的字段合并到slice中
// isArrPath 是否为数组路径
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:27 PM 2022/1/24
func (df *DataFilter) UserItemToSlice() {
df.itemKeyToSlice = true
}
// getValueType 获取数据类型
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/1/23 12:45 AM
func (df *DataFilter) getValueType(valueResult gjson.Result) string {
dataTypeVal := reflect.TypeOf(valueResult.Value())
if nil == dataTypeVal {
return "NIL"
}
dataType := dataTypeVal.String()
if strings.Contains(dataType, "int") {
return "int64"
}
if strings.Contains(dataType, "float") {
return "float64"
}
return dataType
}
// pathIsArrayValue 判断路径是否为数组值
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/1/23 12:56 AM
func (df *DataFilter) pathIsArrayValue(path string) bool {
// Date : 16:00 2023/9/1
func (df *DataFilter) isArrPath(path string) bool {
return strings.Contains(path, "[]")
}
// formatRule 格式化映射规则
// setSameDeepArr 展开同深度数组的
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:43 PM 2022/1/24
func (df *DataFilter) formatRule() {
mapKeyCnt := make(map[string]int)
for _, item := range df.filterRule {
// source 为数组, map 不是
if df.pathIsArrayValue(item.SourceKey) {
if !df.pathIsArrayValue(item.MapKey) {
item.MapKey = item.MapKey + ".[]"
} else {
// source 是数组, map也是数组, 检测数组层级匹配
sourcePathArr := strings.Split(item.SourceKey, ".[].")
mapPathArr := strings.Split(item.MapKey, ".[].")
if len(sourcePathArr) == len(mapPathArr) {
// 数组层级深度相同,无需特殊处理
continue
}
// 数组层级深度不同,重新对对齐数据
diffArr := sourcePathArr[0 : len(sourcePathArr)-len(mapPathArr)+1]
if newPath := df.dealDiffArr(diffArr); len(newPath) > 0 {
sourcePathArr[len(sourcePathArr)-len(mapPathArr)] = newPath
item.SourceKey = strings.Join(sourcePathArr[len(sourcePathArr)-len(mapPathArr):], ".[].")
}
}
// Date : 12:19 2023/9/2
func (df *DataFilter) unfoldSameDeepArr(sourcePath string, mapPath string, sourceRootPath string, mapRootPath string) ([]*FilterDataRule, error) {
df.logPrint(logLevelDebug, " 同深数组展开", sourceRootPath, mapRootPath)
result := make([]*FilterDataRule, 0)
if len(sourcePath) == 0 {
return result, nil
}
sourcePathArr := df.getArrPathList(sourcePath)
mapPathArr := df.getArrPathList(mapPath)
if 1 == len(mapPathArr) {
sourceKey := sourceRootPath + "." + sourcePathArr[0]
if len(sourcePathArr[0:]) > 0 {
sourceKey = sourceRootPath + "." + strings.Join(sourcePathArr[0:], ".[].")
}
result = append(result, &FilterDataRule{
SourceKey: sourceKey,
MapKey: mapRootPath + "." + mapPathArr[0],
DefaultValue: nil,
WithDefault: false,
})
return result, nil
}
// 数组 展开
for idx := 0; idx < len(mapPathArr); idx++ {
if len(sourceRootPath) > 0 {
sourceRootPath = fmt.Sprintf("%v.%v", sourceRootPath, sourcePathArr[idx])
} else {
if df.pathIsArrayValue(item.MapKey) {
continue
}
// source 不是数组, map 也不是
if !df.itemKeyToSlice {
continue
}
mapKeyCnt[item.MapKey]++
sourceRootPath = sourcePathArr[idx]
}
}
// 多个source指向一个map,自动转化为list
for _, item := range df.filterRule {
if mapKeyCnt[item.MapKey] > 1 {
item.MapKey = item.MapKey + ".[]"
}
}
}
// dealDiffArr 提取数据映射关系
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:04 下午 2022/1/25
func (df *DataFilter) dealDiffArr(diffArr []string) string {
if len(diffArr) == 0 {
return ""
}
diffArrStr := strings.Join(diffArr, ".[].")
if _, exist := df.hasDealDiffPath[diffArrStr]; exist {
// 已经处理过, 不再重复处理
return df.hasDealDiffPath[diffArrStr]
}
// 没处理过, 开始处理
jsonResultList := df.getArrayData(df.source, diffArr)
if len(jsonResultList) == 0 {
return ""
}
newPath := util.String.GenRandom("", 8)
var result map[string]interface{}
_ = json.Unmarshal([]byte(df.source), &result)
JSONObject, _ := gabs.Consume(result)
_, _ = JSONObject.ArrayP(newPath)
for _, item := range jsonResultList {
if err := JSONObject.ArrayAppendP(item.Value(), newPath); nil != err {
fmt.Println(err.Error())
}
}
df.source = JSONObject.String()
df.hasDealDiffPath[diffArrStr] = newPath
return newPath
}
// getArrayData 获取数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:22 下午 2022/1/26
func (df *DataFilter) getArrayData(source string, pathArr []string) []gjson.Result {
if len(pathArr) == 1 {
return gjson.Get(source, pathArr[0]).Array()
}
resultList := make([]gjson.Result, 0)
dataList := gjson.Get(source, pathArr[0]).Array()
for idx := 0; idx < len(dataList); idx++ {
resultList = append(resultList, df.getArrayData(dataList[idx].String(), pathArr[1:])...)
}
return resultList
}
// SetArrayData 设置数组数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:05 下午 2022/2/2
func (df *DataFilter) SetArrayData(sourceData string, jsonObject *gabs.Container, sourcePathArr []string, mapPathArr []string) *gabs.Container {
jsonObject = gabs.New()
for idx, sourcePath := range sourcePathArr {
if idx < len(sourcePathArr)-1 {
if !jsonObject.Exists(sourcePath) {
_, _ = jsonObject.ArrayP(sourcePath)
}
}
instance, _ := gabs.ParseJSON([]byte(sourceData))
if !instance.Exists() {
fmt.Println(sourcePathArr[len(sourcePathArr)-1] + " 不存在")
if len(mapRootPath) > 0 {
mapRootPath = fmt.Sprintf("%v.%v", mapRootPath, mapPathArr[idx])
} else {
dataList, _ := instance.Children()
for _, item := range dataList {
cItem := gabs.New()
cItem.SetP(gjson.Get(item.String(), sourcePath).String(), mapPathArr[idx])
jsonObject.ArrayAppendP(cItem.Data(), mapPathArr[idx])
mapRootPath = mapPathArr[idx]
}
valList := gjson.Get(df.source, sourceRootPath)
if valList.Value() == nil {
continue
}
for i := 0; i < len(valList.Array()); i++ {
result = append(result, &FilterDataRule{
SourceKey: sourceRootPath,
MapKey: mapRootPath,
DefaultValue: nil,
WithDefault: false,
})
r, e := df.unfoldSameDeepArr(
strings.Join(sourcePathArr[idx+1:], "[]"),
strings.Join(mapPathArr[idx+1:], "[]"),
fmt.Sprintf("%v.%v", sourceRootPath, i),
fmt.Sprintf("%v.%v", mapRootPath, i),
)
if nil != e {
return nil, e
}
//jsonObject.ArrayAppend(jsonObject.Data())
// fmt.Println("数据 : ", jsonObject.String())
// jsonObject.ArrayAppendP(result.Data(), mapPathArr[idx])
result = append(result, r...)
}
}
return result, nil
}
// a.[].b.[].c.[].d
// g
// getDataAsSlice 抽取制定深度生成list
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:17 2023/9/2
func (df *DataFilter) getDataAsSlice(sourceData string, pathList []string) []interface{} {
//fmt.Println(sourceData, pathList)
result := make([]interface{}, 0)
if len(pathList) == 0 {
return result
}
if len(pathList) == 1 {
return []interface{}{gjson.Get(sourceData, pathList[0]).Value()}
}
for idx, itemPath := range pathList {
if len(pathList)-1 == idx {
val := gjson.Get(sourceData, itemPath).Value()
if nil == val {
return result
}
result = append(result, val)
return result
}
df.SetArrayData(gjson.Get(sourceData, sourcePathArr[idx]).String(), jsonObject, sourcePathArr[idx+1:], mapPathArr[idx+1:])
// jsonObject.ArrayAppendP(v.Data(), mapPathArr[idx])
currentPathVal := gjson.Get(sourceData, itemPath).Array()
for _, sonItem := range currentPathVal {
result = append(result, df.getDataAsSlice(sonItem.String(), pathList[idx:])...)
}
}
fmt.Println("最终 : ", jsonObject.String())
return jsonObject
return result
}
// setValue 设置值
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 22:04 2023/9/1
func (df *DataFilter) setValue(path string, value interface{}) error {
var (
err error
)
df.rewriteResult, err = sjson.Set(df.rewriteResult, path, value)
return err
}
// setKV 设置相关值
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:03 2023/9/1
func (df *DataFilter) setKV(rule *FilterDataRule) error {
var (
err error
)
sourceValue := gjson.Get(df.source, rule.SourceKey)
if sourceValue.Exists() {
// 原始数据存在对应路径
df.rewriteResult, err = sjson.Set(df.rewriteResult, rule.MapKey, sourceValue.Value())
return err
}
if !rule.WithDefault {
// 路径不存在, 且禁用默认值
return errors.New(rule.SourceKey + " : source path not found, and default value is forbidden")
}
// 使用默认值填充
df.rewriteResult, err = sjson.Set(df.rewriteResult, rule.MapKey, rule.DefaultValue)
return err
}
// getArrPathList 获取路径列表
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:32 2023/9/1
func (df *DataFilter) getArrPathList(inputPath string) []string {
pathArr := strings.Split(inputPath, "[]")
arr := make([]string, 0)
for _, item := range pathArr {
arr = append(arr, strings.Trim(item, "."))
}
return arr
}
// logPrint 打印日志
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:00 2023/9/1
func (df *DataFilter) logPrint(level string, msg string, logAttr ...interface{}) {
if !df.filterOption.DebugModel {
// 未开启调试模式
return
}
logData := append([]interface{}{level, msg}, logAttr...)
log.Println(logData...)
}
// 日志等级定义
const (
logLevelPanic = "PANIC"
logLevelFatal = "FATAL"
logLevelWarn = "WARN"
logLevelInfo = "INFO"
logLevelDebug = "DEBUG"
)

285
tool/gabs_test.go Normal file
View File

@ -0,0 +1,285 @@
// Package json_tool ...
//
// Description : json_tool ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2023-09-01 16:07
package json_tool
import (
"encoding/json"
"fmt"
"strings"
"testing"
"git.zhangdeman.cn/zhangdeman/serialize"
)
// TestDataFilter_FilterNormalData 最基础对象
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:13 2023/9/1
func TestDataFilter_FilterNormalData(t *testing.T) {
source := map[string]interface{}{
"name": "zhangdeman",
"extra": map[string]interface{}{
"age": 18,
"height": 180,
"slice": []int{1, 2, 3},
},
"slice": []int{1, 2, 3},
}
df := &DataFilter{
source: serialize.JSON.MarshalForString(source),
filterRule: []*FilterDataRule{
{SourceKey: "name", MapKey: "user_name", DefaultValue: "油猴", WithDefault: true},
{SourceKey: "extra.age", MapKey: "user_age", DefaultValue: "18", WithDefault: true},
{SourceKey: "slice", MapKey: "user_index", DefaultValue: "[4,5,6]", WithDefault: true},
{SourceKey: "none", MapKey: "none_default", DefaultValue: map[string]interface{}{"a": "a"}, WithDefault: true},
{SourceKey: "extra", MapKey: "extra_object", DefaultValue: map[string]interface{}{"a": "a"}, WithDefault: true},
},
filterOption: &FilterOption{DebugModel: true},
}
fmt.Println(df.Filter())
}
// TestDataFilter_getDataAsSlice 测试循环读取数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 22:21 2023/9/1
func TestDataFilter_getDataAsSlice(t *testing.T) {
data := map[string]interface{}{
"list": []interface{}{
map[string]interface{}{
"name": "1",
"age": "2",
"list_test": []interface{}{
map[string]interface{}{
"a": "a",
"b": "b",
},
},
},
map[string]interface{}{
"name": "3",
"age": "4",
"list_test": []interface{}{
map[string]interface{}{
"a": "a1",
"b": "b1",
},
},
},
map[string]interface{}{
"name": "5",
"age": "6",
},
},
}
byteData, _ := json.Marshal(data)
df := &DataFilter{}
fmt.Println(df.getDataAsSlice(string(byteData), []string{"list", "name"}))
fmt.Println(df.getDataAsSlice(string(byteData), []string{"list", "age"}))
fmt.Println(df.getDataAsSlice(string(byteData), []string{"list", "age", "list_test", "a"}))
fmt.Println(df.getDataAsSlice(string(byteData), []string{"list", "age", "list_test", "b"}))
}
// TestDataFilter_sourceArrAndMapSingle map 非数组,数据源数组
//
// Author : zhangdeman001@ke.com<张德满>
//
// Date : 11:24 2023/9/2
func TestDataFilter_sourceArrAndMapSingle(t *testing.T) {
source := map[string]interface{}{
"list": []interface{}{
map[string]interface{}{
"name": "1",
"age": "2",
"list_test": []interface{}{
map[string]interface{}{
"a": "a",
"b": "b",
"c": map[string]interface{}{
"a": "1",
},
"d": []int{1, 2, 3},
},
},
},
map[string]interface{}{
"name": "3",
"age": "4",
"list_test": []interface{}{
map[string]interface{}{
"a": "a1",
"b": "b1",
"c": map[string]interface{}{
"a": "a",
},
"d": []int{1, 2, 3},
},
},
},
map[string]interface{}{
"name": "5",
"age": "6",
},
},
}
df := &DataFilter{
source: serialize.JSON.MarshalForString(source),
filterRule: []*FilterDataRule{
{SourceKey: "list.[].list_test.[].a", MapKey: "a_list", DefaultValue: "[]", WithDefault: true},
{SourceKey: "list.[].list_test.[].b", MapKey: "b_list", DefaultValue: "[]", WithDefault: true},
{SourceKey: "list.[].list_test.[].c", MapKey: "c_list", DefaultValue: "[]", WithDefault: true},
{SourceKey: "list.[].list_test.[].d", MapKey: "d_list", DefaultValue: "[]", WithDefault: true},
},
filterOption: &FilterOption{DebugModel: true},
}
fmt.Println(df.Filter())
}
// TestDataFilter_unfoldSameDeepArr 测试展开同深度数组
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:42 2023/9/2
func TestDataFilter_unfoldSameDeepArr(t *testing.T) {
source := map[string]interface{}{
"list": []interface{}{
map[string]interface{}{
"name": "1",
"age": "2",
"list_test": []interface{}{
map[string]interface{}{
"a": "a",
"b": "b",
"c": map[string]interface{}{
"a": "1",
},
"d": []int{1, 2, 3},
},
},
},
map[string]interface{}{
"name": "3",
"age": "4",
"list_test": []interface{}{
map[string]interface{}{
"a": "a1",
"b": "b1",
"c": map[string]interface{}{
"a": "a",
},
"d": []int{1, 2, 3},
},
map[string]interface{}{
"a": "a2",
"b": "b1",
"c": map[string]interface{}{
"a": "a",
},
"d": []int{1, 2, 3},
},
},
},
map[string]interface{}{
"name": "5",
"age": "6",
},
},
}
df := &DataFilter{
source: serialize.JSON.MarshalForString(source),
filterRule: []*FilterDataRule{
{SourceKey: "list.[].list_test.[].a", MapKey: "a_list.[].a.[].val", DefaultValue: "[]", WithDefault: true},
{SourceKey: "list.[].list_test.[].b", MapKey: "b_list.[].b.[].val", DefaultValue: "[]", WithDefault: true},
{SourceKey: "list.[].list_test.[].c", MapKey: "c_list.[].c.[].val", DefaultValue: "[]", WithDefault: true},
{SourceKey: "list.[].list_test.[].d", MapKey: "d_list.[].d.[].val", DefaultValue: "[]", WithDefault: true},
},
filterOption: &FilterOption{DebugModel: true},
}
r, _ := df.unfoldSameDeepArr("list.[].list_test.[].a", "a_list.[].a_not_equal_list", "", "")
formatRes := make([]*FilterDataRule, 0)
for _, item := range r {
itemMapArr := strings.Split(item.MapKey, ".")
if len(itemMapArr) != 3 && len(itemMapArr) != 5 {
continue
}
formatRes = append(formatRes, item)
}
fmt.Println(serialize.JSON.MarshalForString(formatRes))
}
// TestDataFilter_filterSameDeepArr ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:41 2023/9/2
func TestDataFilter_filterSameDeepArr(t *testing.T) {
source := map[string]interface{}{
"list": []interface{}{
map[string]interface{}{
"name": "1",
"age": "2",
"list_test": []interface{}{
map[string]interface{}{
"a": "a",
"b": "b",
"c": map[string]interface{}{
"a": "1",
},
"d": []int{1, 2, 3},
},
},
},
map[string]interface{}{
"name": "3",
"age": "4",
"list_test": []interface{}{
map[string]interface{}{
"a": "a1",
"b": "b1",
"c": map[string]interface{}{
"a": "a",
},
"d": []int{1, 2, 3},
},
map[string]interface{}{
"a": "a2",
"b": "b1",
"c": map[string]interface{}{
"a": "a",
},
"d": []int{1, 2, 3},
},
},
},
map[string]interface{}{
"name": "5",
"age": "6",
},
},
}
filterRuleList := []*FilterDataRule{
{SourceKey: "name", MapKey: "user_name", DefaultValue: "油猴", WithDefault: true},
{SourceKey: "extra.age", MapKey: "user_age", DefaultValue: "18", WithDefault: true},
{SourceKey: "slice", MapKey: "user_index", DefaultValue: "[4,5,6]", WithDefault: true},
{SourceKey: "none", MapKey: "none_default", DefaultValue: map[string]interface{}{"a": "a"}, WithDefault: true},
{SourceKey: "extra", MapKey: "extra_object", DefaultValue: map[string]interface{}{"a": "a"}, WithDefault: true},
{SourceKey: "list.[].list_test.[].a", MapKey: "a_list.[].a.[].val", DefaultValue: "[]", WithDefault: true},
{SourceKey: "list.[].list_test.[].a", MapKey: "a_list.[].a_not_equal_list", DefaultValue: "[]", WithDefault: true},
{SourceKey: "list.[].list_test.[].a", MapKey: "a_list.[].a.[].val1", DefaultValue: "[]", WithDefault: true},
{SourceKey: "list.[].list_test.[].b", MapKey: "b_list.[].b.[].val", DefaultValue: "[]", WithDefault: true},
{SourceKey: "list.[].list_test.[].c", MapKey: "c_list.[].c.[].val", DefaultValue: "[]", WithDefault: true},
{SourceKey: "list.[].list_test.[].d", MapKey: "d_list.[].d.[].val", DefaultValue: "[]", WithDefault: true},
}
filterOption := &FilterOption{DebugModel: true}
df := NewDataFilter(serialize.JSON.MarshalForString(source), filterRuleList, filterOption)
_, _ = df.Filter()
}

View File

@ -0,0 +1,497 @@
// Package gojson ...
//
// Description : json_tool ...
//
// Author : go_developer@163.com<张德满>
//
// Date : 2022-01-09 10:48 PM
package gojson
import (
"bytes"
"encoding/json"
"fmt"
"go/format"
"io"
"math"
"reflect"
"sort"
"strconv"
"strings"
"unicode"
"gopkg.in/yaml.v3"
)
var ForceFloats bool
// commonInitialisms is a set of common initialisms.
// Only add entries that are highly unlikely to be non-initialisms.
// For instance, "ID" is fine (Freudian code is rare), but "AND" is not.
var commonInitialisms = map[string]bool{
"API": true,
"ASCII": true,
"CPU": true,
"CSS": true,
"DNS": true,
"EOF": true,
"GUID": true,
"HTML": true,
"HTTP": true,
"HTTPS": true,
"ID": true,
"IP": true,
"JSON": true,
"LHS": true,
"QPS": true,
"RAM": true,
"RHS": true,
"RPC": true,
"SLA": true,
"SMTP": true,
"SSH": true,
"TLS": true,
"TTL": true,
"UI": true,
"UID": true,
"UUID": true,
"URI": true,
"URL": true,
"UTF8": true,
"VM": true,
"XML": true,
"NTP": true,
"DB": true,
}
var intToWordMap = []string{
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
}
type Parser func(io.Reader) (interface{}, error)
func ParseJson(input io.Reader) (interface{}, error) {
var result interface{}
if err := json.NewDecoder(input).Decode(&result); err != nil {
return nil, err
}
return result, nil
}
func ParseYaml(input io.Reader) (interface{}, error) {
var result interface{}
b, err := readFile(input)
if err != nil {
return nil, err
}
if err := yaml.Unmarshal(b, &result); err != nil {
return nil, err
}
return result, nil
}
func readFile(input io.Reader) ([]byte, error) {
buf := bytes.NewBuffer(nil)
_, err := io.Copy(buf, input)
if err != nil {
return []byte{}, nil
}
return buf.Bytes(), nil
}
// Generate a struct definition given a JSON string representation of an object and a name structName.
func Generate(input io.Reader, parser Parser, structName, pkgName string, tags []string, subStruct bool, convertFloats bool) ([]byte, error) {
var subStructMap map[string]string = nil
if subStruct {
subStructMap = make(map[string]string)
}
var result map[string]interface{}
iresult, err := parser(input)
if err != nil {
return nil, err
}
switch iresult := iresult.(type) {
case map[interface{}]interface{}:
result = convertKeysToStrings(iresult)
case map[string]interface{}:
result = iresult
case []interface{}:
src := fmt.Sprintf("package %s\n\ntype %s %s\n",
pkgName,
structName,
typeForValue(iresult, structName, tags, subStructMap, convertFloats))
formatted, err := format.Source([]byte(src))
if err != nil {
err = fmt.Errorf("error formatting: %s, was formatting\n%s", err, src)
}
return formatted, err
default:
return nil, fmt.Errorf("unexpected type: %T", iresult)
}
src := fmt.Sprintf("package %s\ntype %s %s}",
pkgName,
structName,
generateTypes(result, structName, tags, 0, subStructMap, convertFloats))
keys := make([]string, 0, len(subStructMap))
for key := range subStructMap {
keys = append(keys, key)
}
sort.Strings(keys)
for _, k := range keys {
src = fmt.Sprintf("%v\n\ntype %v %v", src, subStructMap[k], k)
}
formatted, err := format.Source([]byte(src))
if err != nil {
err = fmt.Errorf("error formatting: %s, was formatting\n%s", err, src)
}
return formatted, err
}
func convertKeysToStrings(obj map[interface{}]interface{}) map[string]interface{} {
res := make(map[string]interface{})
for k, v := range obj {
res[fmt.Sprintf("%v", k)] = v
}
return res
}
// Generate go struct entries for a map[string]interface{} structure
func generateTypes(obj map[string]interface{}, structName string, tags []string, depth int, subStructMap map[string]string, convertFloats bool) string {
structure := "struct {"
keys := make([]string, 0, len(obj))
for key := range obj {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
value := obj[key]
valueType := typeForValue(value, structName, tags, subStructMap, convertFloats)
//value = mergeElements(value)
//If a nested value, recurse
switch value := value.(type) {
case []interface{}:
if len(value) > 0 {
sub := ""
if v, ok := value[0].(map[interface{}]interface{}); ok {
sub = generateTypes(convertKeysToStrings(v), structName, tags, depth+1, subStructMap, convertFloats) + "}"
} else if v, ok := value[0].(map[string]interface{}); ok {
sub = generateTypes(v, structName, tags, depth+1, subStructMap, convertFloats) + "}"
}
if sub != "" {
subName := sub
if subStructMap != nil {
if val, ok := subStructMap[sub]; ok {
subName = val
} else {
subName = fmt.Sprintf("%v_sub%v", structName, len(subStructMap)+1)
subStructMap[sub] = subName
}
}
valueType = "[]" + subName
}
}
case map[interface{}]interface{}:
sub := generateTypes(convertKeysToStrings(value), structName, tags, depth+1, subStructMap, convertFloats) + "}"
subName := sub
if subStructMap != nil {
if val, ok := subStructMap[sub]; ok {
subName = val
} else {
subName = fmt.Sprintf("%v_sub%v", structName, len(subStructMap)+1)
subStructMap[sub] = subName
}
}
valueType = subName
case map[string]interface{}:
sub := generateTypes(value, structName, tags, depth+1, subStructMap, convertFloats) + "}"
subName := sub
if subStructMap != nil {
if val, ok := subStructMap[sub]; ok {
subName = val
} else {
subName = fmt.Sprintf("%v_sub%v", structName, len(subStructMap)+1)
subStructMap[sub] = subName
}
}
valueType = subName
}
fieldName := FmtFieldName(key)
tagList := make([]string, 0)
for _, t := range tags {
tagList = append(tagList, fmt.Sprintf("%s:\"%s\"", t, key))
}
structure += fmt.Sprintf("\n%s %s `%s`",
fieldName,
valueType,
strings.Join(tagList, " "))
}
return structure
}
// FmtFieldName formats a string as a struct key
//
// Example:
//
// FmtFieldName("foo_id")
//
// Output: FooID
func FmtFieldName(s string) string {
runes := []rune(s)
for len(runes) > 0 && !unicode.IsLetter(runes[0]) && !unicode.IsDigit(runes[0]) {
runes = runes[1:]
}
if len(runes) == 0 {
return "_"
}
s = stringifyFirstChar(string(runes))
name := lintFieldName(s)
runes = []rune(name)
for i, c := range runes {
ok := unicode.IsLetter(c) || unicode.IsDigit(c)
if i == 0 {
ok = unicode.IsLetter(c)
}
if !ok {
runes[i] = '_'
}
}
s = string(runes)
s = strings.Trim(s, "_")
if len(s) == 0 {
return "_"
}
return s
}
func lintFieldName(name string) string {
// Fast path for simple cases: "_" and all lowercase.
if name == "_" {
return name
}
allLower := true
for _, r := range name {
if !unicode.IsLower(r) {
allLower = false
break
}
}
if allLower {
runes := []rune(name)
if u := strings.ToUpper(name); commonInitialisms[u] {
copy(runes[0:], []rune(u))
} else {
runes[0] = unicode.ToUpper(runes[0])
}
return string(runes)
}
allUpperWithUnderscore := true
for _, r := range name {
if !unicode.IsUpper(r) && r != '_' {
allUpperWithUnderscore = false
break
}
}
if allUpperWithUnderscore {
name = strings.ToLower(name)
}
// Split camelCase at any lower->upper transition, and split on underscores.
// Check each word for common initialisms.
runes := []rune(name)
w, i := 0, 0 // index of start of word, scan
for i+1 <= len(runes) {
eow := false // whether we hit the end of a word
if i+1 == len(runes) {
eow = true
} else if runes[i+1] == '_' {
// underscore; shift the remainder forward over any run of underscores
eow = true
n := 1
for i+n+1 < len(runes) && runes[i+n+1] == '_' {
n++
}
// Leave at most one underscore if the underscore is between two digits
if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {
n--
}
copy(runes[i+1:], runes[i+n+1:])
runes = runes[:len(runes)-n]
} else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {
// lower->non-lower
eow = true
}
i++
if !eow {
continue
}
// [w,i) is a word.
word := string(runes[w:i])
if u := strings.ToUpper(word); commonInitialisms[u] {
// All the common initialisms are ASCII,
// so we can replace the bytes exactly.
copy(runes[w:], []rune(u))
} else if strings.ToLower(word) == word {
// already all lowercase, and not the first word, so uppercase the first character.
runes[w] = unicode.ToUpper(runes[w])
}
w = i
}
return string(runes)
}
// generate an appropriate struct type entry
func typeForValue(value interface{}, structName string, tags []string, subStructMap map[string]string, convertFloats bool) string {
//Check if this is an array
if objects, ok := value.([]interface{}); ok {
types := make(map[reflect.Type]bool, 0)
for _, o := range objects {
types[reflect.TypeOf(o)] = true
}
if len(types) == 1 {
return "[]" + typeForValue(mergeElements(objects).([]interface{})[0], structName, tags, subStructMap, convertFloats)
}
return "[]interface{}"
} else if object, ok := value.(map[interface{}]interface{}); ok {
return generateTypes(convertKeysToStrings(object), structName, tags, 0, subStructMap, convertFloats) + "}"
} else if object, ok := value.(map[string]interface{}); ok {
return generateTypes(object, structName, tags, 0, subStructMap, convertFloats) + "}"
} else if reflect.TypeOf(value) == nil {
return "interface{}"
}
v := reflect.TypeOf(value).Name()
if v == "float64" && convertFloats {
v = disambiguateFloatInt(value)
}
return v
}
// All numbers will initially be read as float64
// If the number appears to be an integer value, use int instead
func disambiguateFloatInt(value interface{}) string {
const epsilon = .0001
vfloat := value.(float64)
if !ForceFloats && math.Abs(vfloat-math.Floor(vfloat+epsilon)) < epsilon {
var tmp int64
return reflect.TypeOf(tmp).Name()
}
return reflect.TypeOf(value).Name()
}
// convert first character ints to strings
func stringifyFirstChar(str string) string {
first := str[:1]
i, err := strconv.ParseInt(first, 10, 8)
if err != nil {
return str
}
return intToWordMap[i] + "_" + str[1:]
}
func mergeElements(i interface{}) interface{} {
switch i := i.(type) {
default:
return i
case []interface{}:
l := len(i)
if l == 0 {
return i
}
for j := 1; j < l; j++ {
i[0] = mergeObjects(i[0], i[j])
}
return i[0:1]
}
}
func mergeObjects(o1, o2 interface{}) interface{} {
if o1 == nil {
return o2
}
if o2 == nil {
return o1
}
if reflect.TypeOf(o1) != reflect.TypeOf(o2) {
return nil
}
switch i := o1.(type) {
default:
return o1
case []interface{}:
if i2, ok := o2.([]interface{}); ok {
i3 := append(i, i2...)
return mergeElements(i3)
}
return mergeElements(i)
case map[string]interface{}:
if i2, ok := o2.(map[string]interface{}); ok {
for k, v := range i2 {
if v2, ok := i[k]; ok {
i[k] = mergeObjects(v2, v)
} else {
i[k] = v
}
}
}
return i
case map[interface{}]interface{}:
if i2, ok := o2.(map[interface{}]interface{}); ok {
for k, v := range i2 {
if v2, ok := i[k]; ok {
i[k] = mergeObjects(v2, v)
} else {
i[k] = v
}
}
}
return i
}
}

View File

@ -42,15 +42,6 @@ func TestJSON(t *testing.T) {
fmt.Println(tree.String())
}
// TestType 判断数据类型断言
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:59 下午 2021/3/14
func TestType(t *testing.T) {
}
// TestSelect 测试动态选择字段
//
// Author : go_developer@163.com<白茶清欢>
@ -127,151 +118,3 @@ func TestParse(t *testing.T) {
byteData, _ := json.Marshal(source)
fmt.Println(GetJSONDataStruct(string(byteData)))
}
// TestParseWithType 测试获取JSON数据结构
//
// Author : go_developer@163.com<张德满>
//
// Date : 10:59 PM 2022/1/9
func TestParseWithType(t *testing.T) {
source := map[string]interface{}{
"name": "zhangdeman",
"extra": map[string]interface{}{
"age": 18,
"height": 180,
"slice": []int{1, 2, 3},
"obj": map[string]interface{}{
"la": "aaaa",
},
},
"slice": []int{1, 2, 3},
"map": map[string]interface{}{"a": 1, "d": 5.5, "e": "qqq"},
"empty_obj": map[string]interface{}{},
"empty_list": make([]interface{}, 0),
"table": []map[string]interface{}{
{"name": "alex", "age": 18, "number": 1, "obj": map[string]interface{}{"enen": "en"}},
{"name": "bob", "age": 28, "number": 2},
},
"two_slice": []map[string]interface{}{
{
"students": []map[string]interface{}{
{
"name": "enen",
"age": 18,
"score": []float64{1, 2, 3, 45},
},
},
"other": []interface{}{"others"},
"read_only": 1,
},
},
}
byteData, _ := json.Marshal(source)
fmt.Println(GetJSONDataStructWithType(string(byteData)))
}
// TestDataFilter 测试数据过滤
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/1/22 10:19 PM
func TestDataFilter(t *testing.T) {
source := map[string]interface{}{
"name": "zhangdeman",
"extra": map[string]interface{}{
"age": 18,
"height": 180,
"slice": []int{1, 2, 3},
},
"slice_data": []int{1, 2, 3},
"map": map[string]interface{}{"a": 1, "b": 2, "c": 4},
"table": []map[string]interface{}{
{"name": "alex", "age": 18, "number": 1},
{"name": "bob", "age": 28, "number": 2},
{"name": "bob", "age": 28, "number": 2, "list": []int{1, 2, 3}},
},
}
rule := []*FilterDataRule{
{SourceKey: "name", MapKey: "user_name", DefaultValue: "用户姓名默认值"},
{SourceKey: "name", MapKey: "username", DefaultValue: "用户姓名默认值"},
{SourceKey: "name", MapKey: "user.name", DefaultValue: "用户姓名默认值"},
{SourceKey: "extra.age", MapKey: "user.age", DefaultValue: "用户年龄默认值"},
{SourceKey: "extra.age", MapKey: "user_age", DefaultValue: "用户年龄默认值"},
{SourceKey: "extra.height", MapKey: "user.height", DefaultValue: "扩展高度默认值"},
{SourceKey: "extra.height", MapKey: "user_height", DefaultValue: "扩展高度默认值"},
{SourceKey: "table.[].name", MapKey: "slice.[].name_modify", DefaultValue: "列表姓名默认值"},
{SourceKey: "table.[].list", MapKey: "slice.[].data_list", DefaultValue: "[\"567\",\"678\",\"789\"]"},
}
byteData, _ := json.Marshal(source)
filter := NewDataFilter(string(byteData), rule)
fmt.Println(filter.Filter())
}
// TestDataFilterForObiToSlice ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/1/23 12:06 AM
func TestDataFilterForObiToSlice(t *testing.T) {
source := map[string]interface{}{
"name": "zhangdeman",
"age": 18,
"height": 180,
"extra": map[string]interface{}{
"age": 18,
"height": 180,
"slice": []int{1, 2, 3},
},
"slice_data": []int{1, 2, 3},
"map": map[string]interface{}{"a": 1, "b": 2, "c": 4},
"table": []map[string]interface{}{
{"name": "alex", "age": 18, "number": 1},
{"name": "bob", "age": 28, "number": 2},
{"name": "bob", "age": 28, "number": 2, "list": []int{1, 2, 3}},
},
}
rule := []*FilterDataRule{
// {SourceKey: "name", MapKey: "slice.[]", DefaultValue: "用户姓名默认值"},
{SourceKey: "age", MapKey: "slice", DefaultValue: "用户姓名默认值"},
{SourceKey: "height", MapKey: "slice", DefaultValue: "用户姓名默认值"},
}
byteData, _ := json.Marshal(source)
filter := NewDataFilter(string(byteData), rule)
filter.UserItemToSlice()
fmt.Println(filter.Filter())
}
// TestDataFilterDiffArr ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:27 下午 2022/1/26
func TestDataFilterDiffArr(t *testing.T) {
source := map[string]interface{}{
"name": "zhangdeman",
"age": 18,
"height": 180,
"extra": map[string]interface{}{
"age": 18,
"height": 180,
"slice": []int{1, 2, 3},
},
"slice_data": []int{1, 2, 3},
"map": map[string]interface{}{"a": 1, "b": 2, "c": 4},
"table": []map[string]interface{}{
{"user_list": []interface{}{map[string]interface{}{"name": "alex", "age": 18, "number": 1}}},
{"user_list": []interface{}{map[string]interface{}{"name": "bob", "age": 28, "number": 2}}},
{"user_list": []interface{}{map[string]interface{}{"name": "andy", "age": 28, "number": 2}}},
},
}
rule := []*FilterDataRule{
// {SourceKey: "name", MapKey: "slice.[]", DefaultValue: "用户姓名默认值"},
{SourceKey: "table.[].user_list.[].name", MapKey: "user_list.[].detail.name", DefaultValue: "用户姓名默认值"},
{SourceKey: "table.[].user_list.[].age", MapKey: "user_list.[].detail.age", DefaultValue: "用户姓名默认值"},
}
byteData, _ := json.Marshal(source)
filter := NewDataFilter(string(byteData), rule)
filter.UserItemToSlice()
filter.Filter()
//fmt.Println()
}

View File

@ -117,7 +117,11 @@ func GetJSONDataStructWithType(data string) ([]Field, error) {
}
pathList := make([]Field, 0)
r := gjson.Parse(data)
inputIsArr := r.IsArray()
r.ForEach(func(key, value gjson.Result) bool {
if inputIsArr && key.Int() > 0 {
return true
}
if value.Value() == nil {
pathList = append(pathList, Field{
Path: key.String(),
@ -183,6 +187,11 @@ func GetJSONDataStructWithType(data string) ([]Field, error) {
return true
})
for idx := 0; idx < len(pathList); idx++ {
if inputIsArr && strings.HasPrefix(pathList[idx].Path, "0.") {
pathList[idx].Path = strings.Replace(pathList[idx].Path, "0.", "[].", 1)
}
}
return pathList, nil
}

View File

@ -9,7 +9,7 @@ package tree
import (
"fmt"
"git.zhangdeman.cn/zhangdeman/util"
"git.zhangdeman.cn/zhangdeman/wrapper"
"strings"
"github.com/pkg/errors"
@ -88,7 +88,7 @@ func (g *Generate) init() error {
fmt.Println(jsonKey, valueType, jsonValue, startIndex)
// 创建节点, 并挂在到树上
var newNode *Node
if util.Array.In(g.currentNode.ValueType, []string{ValueTypeArray, ValueTypeMap}) >= 0 {
if wrapper.ArrayType([]string{ValueTypeArray, ValueTypeMap}).Has(g.currentNode.ValueType) >= 0 {
newNode = NewNode(jsonKey, jsonValue, valueType, g.currentNode)
g.currentParentNode = g.currentNode
g.currentParentNode.SonNodeList = append(g.currentParentNode.SonNodeList, newNode)
@ -116,25 +116,26 @@ func (g *Generate) getKey(startIndex int) (string, int, error) {
continue
}
if charStr == KeywordDoubleQuote && !hasStart {
// 第一次遇见双引号
startIndex++
hasStart = true
continue
}
if charStr == KeywordDoubleQuote && hasStart {
// 第二次遇见双引号key探寻结束
startIndex++
break
if charStr == KeywordDoubleQuote {
if !hasStart {
// 第一次遇见双引号
startIndex++
hasStart = true
continue
} else {
// 第二次遇见双引号key探寻结束
startIndex++
break
}
}
if !hasStart {
if util.Array.In(charStr, []string{
if wrapper.ArrayType([]string{
KeywordDoubleQuote,
KeywordObjectStart,
KeywordArrayStart,
KeywordComma,
}) >= 0 {
}).Has(charStr) >= 0 {
startIndex++
continue
}
@ -181,21 +182,18 @@ func (g *Generate) getValueType(startIndex int) (string, int, error) {
hasFindColon := false
for startIndex < len(g.jsonDataByte) {
charStr := string(g.jsonDataByte[startIndex])
if !hasFindColon {
if charStr == KeywordSpace {
// 跳过空格
startIndex++
continue
}
if charStr != KeywordSpace {
// 非空格
if charStr != KeywordColon {
return "", startIndex, errors.New("value is invalid")
}
startIndex++
hasFindColon = true
break
if charStr == KeywordSpace {
// 跳过空格
startIndex++
continue
} else {
// 非空格
if charStr != KeywordColon {
return "", startIndex, errors.New("value is invalid")
}
startIndex++
hasFindColon = true
break
}
}
@ -264,13 +262,13 @@ func (g *Generate) getValue(startIndex int) (string, int, error) {
continue
}
if !isStart {
if util.Array.In(str, []string{KeywordArrayEnd, KeywordObjectEnd, KeywordComma}) >= 0 {
if wrapper.ArrayType([]string{KeywordArrayEnd, KeywordObjectEnd, KeywordComma}).Has(str) >= 0 {
startIndex++
continue
}
}
if isStart {
if util.Array.In(str, []string{KeywordDoubleQuote, KeywordArrayEnd, KeywordObjectEnd, KeywordComma}) >= 0 {
if wrapper.ArrayType([]string{KeywordDoubleQuote, KeywordArrayEnd, KeywordObjectEnd, KeywordComma}).Has(str) >= 0 {
// 值的拼接已结束
startIndex++
break