[feat] package nazajson: 新增函数CollectNotExistFields,用于收集json中所有不存在的字段

pull/3/head
q191201771 3 years ago
parent 7526dde84e
commit b12435c3aa

@ -10,9 +10,13 @@ package nazajson
import (
"encoding/json"
"errors"
"reflect"
"strings"
)
var ErrJson = errors.New("nazajson: fxxk")
type Json struct {
//raw []byte
m map[string]interface{}
@ -34,6 +38,104 @@ func (j *Json) Exist(path string) bool {
return exist(j.m, path)
}
// ---------------------------------------------------------------------------------------------------------------------
// CollectNotExistFields
//
// @param data json字符串
// @param v 对应的结构体变量或者结构体指针变量。注意并不要求是反序列化赋值后的结构体变量内部主要是获取字段、tag等信息
// @param ignorePrefix 可选参数,可填入不需要收集的字段的前缀。
// 注意,这里只做简单字符串匹配,比如填入`a`,那么所有以`a`开头的全部会过滤掉(不光是`a.[xxx]`,还包含`ab.``ac.`等等)
// 以上语义以后可能会发送变化,建议使用方在字段名字相似的情况下,使用完成的字段名称
//
// @return 返回所有不存在的json字段组成的数组
//
func CollectNotExistFields(data []byte, v interface{}, ignorePrefix ...string) ([]string, error) {
j, err := New(data)
if err != nil {
return nil, err
}
typ := reflect.TypeOf(v)
ret, err := collectNotExistFields(j, "", typ, 0)
if err != nil || len(ignorePrefix) == 0 {
return ret, err
}
var filterRet []string
for _, notExistField := range ret {
shouldIgnore := false
for _, ignorePrefixItem := range ignorePrefix {
if strings.HasPrefix(notExistField, ignorePrefixItem) {
shouldIgnore = true
break
}
}
if !shouldIgnore {
filterRet = append(filterRet, notExistField)
}
}
return filterRet, nil
}
// ---------------------------------------------------------------------------------------------------------------------
// @param prefix 判断json是否存在的路径前缀如果没有设置为""
// 注意,路径可以是多级,但是最后的字符不需要`.`
// @param debugDepth 递归调用层级,调试时使用
//
func collectNotExistFields(j Json, prefix string, typ reflect.Type, debugDepth int) (notExists []string, err error) {
//nazalog.Debugf("[%d] > collectNotExistFields. typ=%+v", debugDepth, typ)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if typ.Kind() != reflect.Struct {
return nil, ErrJson
}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
//nazalog.Debugf("[%d] iterate field=%+v", debugDepth, field)
if field.Anonymous {
anonyNotExists, err := collectNotExistFields(j, prefix, field.Type, debugDepth+1)
if err != nil {
return anonyNotExists, err
}
notExists = append(notExists, anonyNotExists...)
}
// json的字段名
jk, ok := field.Tag.Lookup("json")
if !ok {
// 字段没有json tag直接忽略
continue
}
if prefix != "" {
jk = prefix + "." + jk
}
ok = j.Exist(jk)
if !ok {
// 注意,如果这一层的字段是结构体类型,且不存在,那么该结构体的所有字段都将不存在
// 此时有多种收集方式
// 我们的做法是选择不收集结构体自身,而是收集该结构体的所有字段
if field.Type.Kind() != reflect.Struct {
notExists = append(notExists, jk)
}
}
//nazalog.Debugf("[%d] check exist result. ok=%+v, prefix=%s, jk=%s", debugDepth, ok, prefix, jk)
if field.Type.Kind() == reflect.Struct {
subNotExists, err := collectNotExistFields(j, jk, field.Type, debugDepth+1)
if err != nil {
return subNotExists, err
}
notExists = append(notExists, subNotExists...)
}
}
//nazalog.Debugf("[%d] end. collect=%+v", debugDepth, notExists)
return
}
// 判断`m`的`path`是否存在
func exist(m map[string]interface{}, path string) bool {
ps := strings.Split(path, ".")

@ -9,6 +9,7 @@
package nazajson
import (
"encoding/json"
"testing"
"github.com/q191201771/naza/pkg/assert"
@ -125,3 +126,69 @@ func BenchmarkExist(b *testing.B) {
}
nazalog.Debug(exist)
}
func TestCollectNotExistFields(t *testing.T) {
// 1. 测试自身的基础字段 完成
// 1.2 测试数组 完成
// 2. 测试自身的指针基础字段 完成
// 3. 测试匿名结构体 完成
// 4. 测试嵌套结构体 完成
// 5. 测试没有tag 完成
// 6. 测试小写不暴露的成员 完成
type Sub struct {
SubA int `json:"suba"`
SubB int `json:"subb"`
SubC string `json:"subc"`
SubD string `json:"subd"`
}
type Anoy struct {
AnoyA int `json:"anoya"`
AnoyB int `json:"anoyb"`
}
type St struct {
Anoy
A int `json:"a"`
B string `json:"b"`
C []bool `json:"c"`
D []int `json:"d"`
E *int `json:"e"`
F *string `json:"f"`
Sub Sub `json:"sub"`
NoTag int
low int
}
b := []byte(`
{
"anoya": 3,
"a": 1,
"c": [true, false],
"e": 2,
"sub": {
"suba": 4,
"subc": "c"
}
}
`)
var st St
json.Unmarshal(b, &st)
nazalog.Infof("%+v", st)
//var st2 St
//collect, err := CollectNotExistFields(b, st)
//CollectNotExistFields(b, st2)
//CollectNotExistFields(b, &st2)
// 以上三种使用方式也都是正常的
collect, err := CollectNotExistFields(b, &st)
assert.Equal(t, nil, err)
assert.Equal(t, []string{"anoyb", "b", "d", "f", "sub.subb", "sub.subd"}, collect)
collect, err = CollectNotExistFields(b, st, "sub")
assert.Equal(t, nil, err)
assert.Equal(t, []string{"anoyb", "b", "d", "f"}, collect)
collect, err = CollectNotExistFields(b, st, "s")
assert.Equal(t, nil, err)
assert.Equal(t, []string{"anoyb", "b", "d", "f"}, collect)
}

Loading…
Cancel
Save