Skip to content

fix scan over nil and enforce ordering #4

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

Merged
merged 8 commits into from
May 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
40 changes: 33 additions & 7 deletions jsonpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"go/types"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
)
Expand Down Expand Up @@ -143,6 +144,13 @@ func (c *Compiled) Lookup(obj interface{}) (interface{}, error) {
if err != nil {
return nil, err
}
if obj == nil {
continue
}
// empty scan is NULL
if len(obj.([]interface{})) == 0 {
obj = nil
}
default:
return nil, fmt.Errorf("unsupported jsonpath operation: %s", s.op)
}
Expand Down Expand Up @@ -557,20 +565,38 @@ func get_filtered(obj, root interface{}, filter string) ([]interface{}, error) {

func get_scan(obj interface{}) (interface{}, error) {
if reflect.TypeOf(obj) == nil {
return nil, ErrGetFromNullObj
return nil, nil
}
switch reflect.TypeOf(obj).Kind() {
case reflect.Map:
// iterate over keys in sorted by length, then alphabetically
var res []interface{}
if jsonMap, ok := obj.(map[string]interface{}); ok {
for _, v := range jsonMap {
res = append(res, v)
var sortedKeys []string
for k := range jsonMap {
sortedKeys = append(sortedKeys, k)
}
sort.Slice(sortedKeys, func(i, j int) bool {
if len(sortedKeys[i]) != len(sortedKeys[j]) {
return len(sortedKeys[i]) < len(sortedKeys[j])
}
return sortedKeys[i] < sortedKeys[j]
})
for _, k := range sortedKeys {
res = append(res, jsonMap[k])
}
return res, nil
}
iter := reflect.ValueOf(obj).MapRange()
for iter.Next() {
res = append(res, iter.Value().Interface())
keys := reflect.ValueOf(obj).MapKeys()
sort.Slice(keys, func(i, j int) bool {
ki, kj := keys[i].String(), keys[j].String()
if len(ki) != len(kj) {
return len(ki) < len(kj)
}
return ki < kj
})
for _, k := range keys {
res = append(res, reflect.ValueOf(obj).MapIndex(k).Interface())
}
return res, nil
case reflect.Slice:
Expand All @@ -586,7 +612,7 @@ func get_scan(obj interface{}) (interface{}, error) {
}
return res, nil
default:
return nil, fmt.Errorf("object is not scanable: %v", reflect.TypeOf(obj).Kind())
return nil, fmt.Errorf("object is not scannable: %v", reflect.TypeOf(obj).Kind())
}
}

Expand Down
56 changes: 25 additions & 31 deletions jsonpath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,6 @@ func Test_jsonpath_get_scan(t *testing.T) {
"key": 1,
}
res, err := get_scan(obj)
t.Logf("err: %v, res: %v", err, res)
if err != nil {
t.Errorf("failed to scan: %v", err)
return
Expand All @@ -554,9 +553,8 @@ func Test_jsonpath_get_scan(t *testing.T) {

obj2 := 1
res, err = get_scan(obj2)
t.Logf("err: %v, res: %v", err, res)
if err == nil {
t.Errorf("object is not scanable error not raised")
if err == nil || err.Error() != "object is not scannable: int" {
t.Errorf("object is not scannable error not raised")
return
}

Expand All @@ -573,12 +571,14 @@ func Test_jsonpath_get_scan(t *testing.T) {
if len(res_v) != 3 {
t.Errorf("scanned result is of wrong length")
}
// order of items in maps can't be guaranteed
for _, v := range res_v {
val, _ := v.(string)
if val != "hah1" && val != "hah2" && val != "hah3" {
t.Errorf("scanned result contains unexpected value: %v", val)
}
if v, ok := res_v[0].(string); !ok || v != "hah1" {
t.Errorf("scanned result contains unexpected value: %v", v)
}
if v, ok := res_v[1].(string); !ok || v != "hah2" {
t.Errorf("scanned result contains unexpected value: %v", v)
}
if v, ok := res_v[2].(string); !ok || v != "hah3" {
t.Errorf("scanned result contains unexpected value: %v", v)
}

obj4 := map[string]interface{}{
Expand All @@ -590,37 +590,31 @@ func Test_jsonpath_get_scan(t *testing.T) {
"c": 3,
},
"key4" : []interface{}{1,2,3},
"key5" : nil,
}
res, err = get_scan(obj4)
res_v, ok = res.([]interface{})
if !ok {
t.Errorf("scanned result is not a slice")
}
if len(res_v) != 4 {
if len(res_v) != 5 {
t.Errorf("scanned result is of wrong length")
}
// order of items in maps can't be guaranteed
for _, v := range res_v {
switch v.(type) {
case string:
if v_str, ok := v.(string); ok && v_str == "abc" {
continue
}
case int:
if v_int, ok := v.(int); ok && v_int == 123 {
continue
}
case map[string]interface{}:
if v_map, ok := v.(map[string]interface{}); ok && v_map["a"].(int) == 1 && v_map["b"].(int) == 2 && v_map["c"].(int) == 3 {
continue
}
case []interface{}:
if v_slice, ok := v.([]interface{}); ok && v_slice[0].(int) == 1 && v_slice[1].(int) == 2 && v_slice[2].(int) == 3 {
continue
}
}
if v, ok := res_v[0].(string); !ok || v != "abc" {
t.Errorf("scanned result contains unexpected value: %v", v)
}
if v, ok := res_v[1].(int); !ok || v != 123 {
t.Errorf("scanned result contains unexpected value: %v", v)
}
if v, ok := res_v[2].(map[string]interface{}); !ok || v["a"].(int) != 1 || v["b"].(int) != 2 || v["c"].(int) != 3 {
t.Errorf("scanned result contains unexpected value: %v", v)
}
if v, ok := res_v[3].([]interface{}); !ok || v[0].(int) != 1 || v[1].(int) != 2 || v[2].(int) != 3 {
t.Errorf("scanned result contains unexpected value: %v", v)
}
if res_v[4] != nil {
t.Errorf("scanned result contains unexpected value: %v", res_v[4])
}
}

func Test_jsonpath_types_eval(t *testing.T) {
Expand Down