Skip to content

Commit

Permalink
Merge pull request #1 from tiopramayudi/master
Browse files Browse the repository at this point in the history
Fix null exception and error when key is not present
  • Loading branch information
tiopramayudi authored Dec 15, 2021
2 parents 2e52cf6 + 506cd6a commit 37eac26
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 39 deletions.
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
40 changes: 19 additions & 21 deletions jsonpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,12 @@ 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)
tmp, err := get_idx(obj, x)
if err != nil {
return nil, err
Expand All @@ -98,13 +99,11 @@ func (c *Compiled) Lookup(obj interface{}) (interface{}, error) {
}
obj = res
} else if len(s.args.([]int)) == 1 {
//fmt.Println("idx ----------------3")
obj, err = get_idx(obj, s.args.([]int)[0])
if err != nil {
return nil, err
}
} else {
//fmt.Println("idx ----------------4")
return nil, fmt.Errorf("cannot index on empty slice")
}
case "range":
Expand All @@ -115,7 +114,11 @@ func (c *Compiled) Lookup(obj interface{}) (interface{}, error) {
return nil, err
}
}
if argsv, ok := s.args.([2]interface{}); ok == true {
if obj == nil {
return nil, nil
}

if argsv, ok := s.args.([2]interface{}); ok {
obj, err = get_range(obj, argsv[0], argsv[1])
if err != nil {
return nil, err
Expand Down Expand Up @@ -167,9 +170,7 @@ func tokenize(query string) ([]string, error) {
token = "."
continue
} else {
// fmt.Println("else: ", string(x), token)
if strings.Contains(token, "[") {
// fmt.Println(" contains [ ")
if x == ']' && !strings.HasSuffix(token, "\\]") {
if token[0] == '.' {
tokens = append(tokens, token[1:])
Expand All @@ -180,7 +181,6 @@ func tokenize(query string) ([]string, error) {
continue
}
} else {
// fmt.Println(" doesn't contains [ ")
if x == '.' {
if token[0] == '.' {
tokens = append(tokens, token[1:len(token)-1])
Expand Down Expand Up @@ -209,8 +209,6 @@ func tokenize(query string) ([]string, error) {
}
}
}
// fmt.Println("finished tokens: ", tokens)
// fmt.Println("================================================= done ")
return tokens, nil
}

Expand All @@ -237,7 +235,6 @@ func parse_token(token string) (op string, key string, args interface{}, err err
}
tail = tail[1 : len(tail)-1]

//fmt.Println(key, tail)
if strings.Contains(tail, "?") {
// filter -------------------------------------------------
op = "filter"
Expand Down Expand Up @@ -292,8 +289,6 @@ 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)
if err != nil {
return nil, err
}
Expand All @@ -302,7 +297,6 @@ func filter_get_from_explicit_path(obj interface{}, path string) (interface{}, e
}
steps = steps[1:]
xobj := obj
//fmt.Println("f: xobj", xobj)
for _, s := range steps {
op, key, args, err := parse_token(s)
// "key", "idx"
Expand Down Expand Up @@ -333,7 +327,7 @@ func filter_get_from_explicit_path(obj interface{}, path string) (interface{}, e

func get_key(obj interface{}, key string) (interface{}, error) {
if reflect.TypeOf(obj) == nil {
return nil, ErrGetFromNullObj
return nil, nil
}
switch reflect.TypeOf(obj).Kind() {
case reflect.Map:
Expand All @@ -343,17 +337,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 @@ -395,6 +389,10 @@ func get_range(obj, frm, to interface{}) (interface{}, error) {
switch reflect.TypeOf(obj).Kind() {
case reflect.Slice:
length := reflect.ValueOf(obj).Len()
// if length of slice is 0 return nil instead of out of range error
if length == 0 {
return nil, nil
}
_frm := 0
_to := length
if frm == nil {
Expand Down Expand Up @@ -423,7 +421,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 +666,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 +703,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
54 changes: 36 additions & 18 deletions jsonpath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ func init() {
"price": 19.95
}
},
"expensive": 10
"expensive": 10,
"null_array": null,
"empty_array": []
}
`
json.Unmarshal([]byte(data), &json_data)
Expand All @@ -62,6 +64,22 @@ 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("nulls_array[*].key should be nil")
}

res, err = JsonPathLookup(json_data, "$.empty_array[*].key")
if err != nil || res != nil {
t.Errorf("empty_array[*].key should be nil")
}

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

// 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 +93,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 +114,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 +438,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 +639,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 +700,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 +806,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 +821,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 +865,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 +891,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 +998,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 +1197,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 +1250,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

0 comments on commit 37eac26

Please sign in to comment.