Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix null exception and error when key is not present #1

Merged
merged 2 commits into from
Dec 15, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/gojekfarm/jsonpath

go 1.16
33 changes: 19 additions & 14 deletions jsonpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,13 @@ func (c *Compiled) Lookup(obj interface{}) (interface{}, error) {
return nil, err
}
}

if obj == nil {
return nil, nil
}
if len(s.args.([]int)) > 1 {
res := []interface{}{}
for _, x := range s.args.([]int) {
//fmt.Println("idx ---- ", x)
// fmt.Println("idx ---- ", x)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove all dead code?

tmp, err := get_idx(obj, x)
if err != nil {
return nil, err
Expand All @@ -98,13 +100,13 @@ func (c *Compiled) Lookup(obj interface{}) (interface{}, error) {
}
obj = res
} else if len(s.args.([]int)) == 1 {
//fmt.Println("idx ----------------3")
// fmt.Println("idx ----------------3")
obj, err = get_idx(obj, s.args.([]int)[0])
if err != nil {
return nil, err
}
} else {
//fmt.Println("idx ----------------4")
// fmt.Println("idx ----------------4")
return nil, fmt.Errorf("cannot index on empty slice")
}
case "range":
Expand All @@ -115,6 +117,9 @@ func (c *Compiled) Lookup(obj interface{}) (interface{}, error) {
return nil, err
}
}
if obj == nil {
return nil, nil
}
if argsv, ok := s.args.([2]interface{}); ok == true {
obj, err = get_range(obj, argsv[0], argsv[1])
if err != nil {
Expand Down Expand Up @@ -237,7 +242,7 @@ func parse_token(token string) (op string, key string, args interface{}, err err
}
tail = tail[1 : len(tail)-1]

//fmt.Println(key, tail)
// fmt.Println(key, tail)
if strings.Contains(tail, "?") {
// filter -------------------------------------------------
op = "filter"
Expand Down Expand Up @@ -292,8 +297,8 @@ func parse_token(token string) (op string, key string, args interface{}, err err

func filter_get_from_explicit_path(obj interface{}, path string) (interface{}, error) {
steps, err := tokenize(path)
//fmt.Println("f: steps: ", steps, err)
//fmt.Println(path, steps)
// fmt.Println("f: steps: ", steps, err)
// fmt.Println(path, steps)
if err != nil {
return nil, err
}
Expand All @@ -302,7 +307,7 @@ func filter_get_from_explicit_path(obj interface{}, path string) (interface{}, e
}
steps = steps[1:]
xobj := obj
//fmt.Println("f: xobj", xobj)
// fmt.Println("f: xobj", xobj)
for _, s := range steps {
op, key, args, err := parse_token(s)
// "key", "idx"
Expand Down Expand Up @@ -343,17 +348,17 @@ func get_key(obj interface{}, key string) (interface{}, error) {
if jsonMap, ok := obj.(map[string]interface{}); ok {
val, exists := jsonMap[key]
if !exists {
return nil, fmt.Errorf("key error: %s not found in object", key)
return nil, nil
}
return val, nil
}
for _, kv := range reflect.ValueOf(obj).MapKeys() {
//fmt.Println(kv.String())
// fmt.Println(kv.String())
if kv.String() == key {
return reflect.ValueOf(obj).MapIndex(kv).Interface(), nil
}
}
return nil, fmt.Errorf("key error: %s not found in object", key)
return nil, nil
case reflect.Slice:
// slice we should get from all objects in it.
res := []interface{}{}
Expand Down Expand Up @@ -423,7 +428,7 @@ func get_range(obj, frm, to interface{}) (interface{}, error) {
if _to < 0 || _to > length {
return nil, fmt.Errorf("index [to] out of range: len: %v, to: %v", length, to)
}
//fmt.Println("_frm, _to: ", _frm, _to)
// fmt.Println("_frm, _to: ", _frm, _to)
res_v := reflect.ValueOf(obj).Slice(_frm, _to)
return res_v.Interface(), nil
default:
Expand Down Expand Up @@ -668,7 +673,7 @@ func eval_filter(obj, root interface{}, lp, op, rp string) (res bool, err error)
} else {
rp_v = rp
}
//fmt.Printf("lp_v: %v, rp_v: %v\n", lp_v, rp_v)
// fmt.Printf("lp_v: %v, rp_v: %v\n", lp_v, rp_v)
return cmp_any(lp_v, rp_v, op)
}
}
Expand Down Expand Up @@ -705,7 +710,7 @@ func cmp_any(obj1, obj2 interface{}, op string) (bool, error) {
} else {
exp = fmt.Sprintf(`"%v" %s "%v"`, obj1, op, obj2)
}
//fmt.Println("exp: ", exp)
// fmt.Println("exp: ", exp)
fset := token.NewFileSet()
res, err := types.Eval(fset, nil, 0, exp)
if err != nil {
Expand Down
48 changes: 30 additions & 18 deletions jsonpath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ func init() {
"price": 19.95
}
},
"expensive": 10
"expensive": 10,
"null_array": null
}
`
json.Unmarshal([]byte(data), &json_data)
Expand All @@ -62,6 +63,17 @@ func Test_jsonpath_JsonPathLookup_1(t *testing.T) {
t.Errorf("expensive should be 10")
}

res, err := JsonPathLookup(json_data, "$.nulls_array[*].key")
if err != nil || res != nil {
t.Errorf("expensive shoul")
}

// not found key
res, err = JsonPathLookup(json_data, "$.not_exist_key")
if err != nil || res != nil {
t.Errorf("expensive shoul")
}

// single index
res, _ = JsonPathLookup(json_data, "$.store.book[0].price")
if res_v, ok := res.(float64); ok != true || res_v != 8.95 {
Expand All @@ -75,7 +87,7 @@ func Test_jsonpath_JsonPathLookup_1(t *testing.T) {
}

// multiple index
res, err := JsonPathLookup(json_data, "$.store.book[0,1].price")
res, err = JsonPathLookup(json_data, "$.store.book[0,1].price")
t.Log(err, res)
if res_v, ok := res.([]interface{}); ok != true || res_v[0].(float64) != 8.95 || res_v[1].(float64) != 12.99 {
t.Errorf("exp: [8.95, 12.99], got: %v", res)
Expand All @@ -96,7 +108,7 @@ func Test_jsonpath_JsonPathLookup_1(t *testing.T) {
if res_v, ok := res.([]interface{}); ok != true || res_v[0].(float64) != 8.95 || res_v[1].(float64) != 12.99 || res_v[2].(float64) != 8.99 || res_v[3].(float64) != 22.99 {
t.Errorf("exp: [8.95, 12.99, 8.99, 22.99], got: %v", res)
}

// range
res, err = JsonPathLookup(json_data, "$.store.book[0:1].price")
t.Log(err, res)
Expand Down Expand Up @@ -420,8 +432,8 @@ func Test_jsonpath_get_key(t *testing.T) {

res, err = get_key(obj, "hah")
fmt.Println(err, res)
if err == nil {
t.Errorf("key error not raised")
if err != nil {
t.Errorf("key error should not raised")
return
}
if res != nil {
Expand Down Expand Up @@ -621,8 +633,7 @@ var tcase_parse_filter = []map[string]interface{}{
}

func Test_jsonpath_parse_filter(t *testing.T) {

//for _, tcase := range tcase_parse_filter[4:] {
// for _, tcase := range tcase_parse_filter[4:] {
for _, tcase := range tcase_parse_filter {
lp, op, rp, _ := parse_filter(tcase["filter"].(string))
t.Log(tcase)
Expand Down Expand Up @@ -683,7 +694,6 @@ var tcase_filter_get_from_explicit_path = []map[string]interface{}{
}

func Test_jsonpath_filter_get_from_explicit_path(t *testing.T) {

for idx, tcase := range tcase_filter_get_from_explicit_path {
obj := tcase["obj"]
query := tcase["query"].(string)
Expand Down Expand Up @@ -790,7 +800,6 @@ func Test_jsonpath_eval_filter(t *testing.T) {
exp := tcase["exp"].(bool)
t.Logf("idx: %v, lp: %v, op: %v, rp: %v, exp: %v", idx, lp, op, rp, exp)
got, err := eval_filter(obj, root, lp, op, rp)

if err != nil {
t.Errorf("idx: %v, failed to eval: %v", idx, err)
return
Expand All @@ -806,6 +815,7 @@ var (
ifc1 interface{} = "haha"
ifc2 interface{} = "ha ha"
)

var tcase_cmp_any = []map[string]interface{}{

map[string]interface{}{
Expand Down Expand Up @@ -849,19 +859,22 @@ var tcase_cmp_any = []map[string]interface{}{
"op": "=~",
"exp": false,
"err": "op should only be <, <=, ==, >= and >",
}, {
},
{
"obj1": ifc1,
"obj2": ifc1,
"op": "==",
"exp": true,
"err": nil,
}, {
},
{
"obj1": ifc2,
"obj2": ifc2,
"op": "==",
"exp": true,
"err": nil,
}, {
},
{
"obj1": 20,
"obj2": "100",
"op": ">",
Expand All @@ -872,7 +885,7 @@ var tcase_cmp_any = []map[string]interface{}{

func Test_jsonpath_cmp_any(t *testing.T) {
for idx, tcase := range tcase_cmp_any {
//for idx, tcase := range tcase_cmp_any[8:] {
// for idx, tcase := range tcase_cmp_any[8:] {
t.Logf("idx: %v, %v %v %v, exp: %v", idx, tcase["obj1"], tcase["op"], tcase["obj2"], tcase["exp"])
res, err := cmp_any(tcase["obj1"], tcase["obj2"], tcase["op"].(string))
exp := tcase["exp"].(bool)
Expand Down Expand Up @@ -979,7 +992,6 @@ func Test_jsonpath_num_cmp(t *testing.T) {
if len(arr) != 0 {
t.Fatal("should return [], got: ", arr)
}

}

func BenchmarkJsonPathLookupCompiled(b *testing.B) {
Expand Down Expand Up @@ -1179,13 +1191,13 @@ func Test_jsonpath_rootnode_is_array_range(t *testing.T) {
t.Logf("idx: %v, v: %v", idx, v)
}
if len(ares) != 2 {
t.Fatal("len is not 2. got: %v", len(ares))
t.Fatalf("len is not 2. got: %v", len(ares))
}
if ares[0].(float64) != 12.34 {
t.Fatal("idx: 0, should be 12.34. got: %v", ares[0])
t.Fatalf("idx: 0, should be 12.34. got: %v", ares[0])
}
if ares[1].(float64) != 13.34 {
t.Fatal("idx: 0, should be 12.34. got: %v", ares[1])
t.Fatalf("idx: 0, should be 12.34. got: %v", ares[1])
}
}

Expand Down Expand Up @@ -1232,7 +1244,7 @@ func Test_jsonpath_rootnode_is_nested_array_range(t *testing.T) {
t.Logf("idx: %v, v: %v", idx, v)
}
if len(ares) != 2 {
t.Fatal("len is not 2. got: %v", len(ares))
t.Fatalf("len is not 2. got: %v", len(ares))
}

//FIXME: `$[:1].[0].test` got wrong result
Expand Down