Skip to content

Commit 604d46a

Browse files
authored
Merge pull request #11 from superfell/compile
Add support for compiling a lookup
2 parents 530466c + cc85ae5 commit 604d46a

2 files changed

Lines changed: 70 additions & 16 deletions

File tree

jsonpath.go

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,72 @@ import (
1414
var ErrGetFromNullObj = errors.New("get attribute from null object")
1515

1616
func JsonPathLookup(obj interface{}, jpath string) (interface{}, error) {
17-
steps, err := tokenize(jpath)
17+
c, err := Compile(jpath)
1818
if err != nil {
1919
return nil, err
2020
}
21-
if steps[0] != "@" && steps[0] != "$" {
21+
return c.Lookup(obj)
22+
}
23+
24+
type Compiled struct {
25+
path string
26+
steps []step
27+
}
28+
29+
type step struct {
30+
op string
31+
key string
32+
args interface{}
33+
}
34+
35+
func Compile(jpath string) (*Compiled, error) {
36+
tokens, err := tokenize(jpath)
37+
if err != nil {
38+
return nil, err
39+
}
40+
if tokens[0] != "@" && tokens[0] != "$" {
2241
return nil, fmt.Errorf("$ or @ should in front of path")
2342
}
24-
steps = steps[1:]
25-
for _, s := range steps {
26-
op, key, args, err := parse_token(s)
43+
tokens = tokens[1:]
44+
res := Compiled{
45+
path: jpath,
46+
steps: make([]step, len(tokens)),
47+
}
48+
for i, token := range tokens {
49+
op, key, args, err := parse_token(token)
50+
if err != nil {
51+
return nil, err
52+
}
53+
res.steps[i] = step{op, key, args}
54+
}
55+
return &res, nil
56+
}
57+
58+
func (c *Compiled) String() string {
59+
return fmt.Sprintf("Compiled lookup: %s", c.path)
60+
}
61+
62+
func (c *Compiled) Lookup(obj interface{}) (interface{}, error) {
63+
var err error
64+
for _, s := range c.steps {
2765
// "key", "idx"
28-
switch op {
66+
switch s.op {
2967
case "key":
30-
obj, err = get_key(obj, key)
68+
obj, err = get_key(obj, s.key)
3169
if err != nil {
3270
return nil, err
3371
}
3472
case "idx":
3573
//fmt.Println("idx ----------------1")
36-
obj, err = get_key(obj, key)
74+
obj, err = get_key(obj, s.key)
3775
if err != nil {
3876
return nil, err
3977
}
4078

41-
if len(args.([]int)) > 1 {
79+
if len(s.args.([]int)) > 1 {
4280
//fmt.Println("idx ----------------2")
4381
res := []interface{}{}
44-
for _, x := range args.([]int) {
82+
for _, x := range s.args.([]int) {
4583
//fmt.Println("idx ---- ", x)
4684
tmp, err := get_idx(obj, x)
4785
if err != nil {
@@ -50,9 +88,9 @@ func JsonPathLookup(obj interface{}, jpath string) (interface{}, error) {
5088
res = append(res, tmp)
5189
}
5290
obj = res
53-
} else if len(args.([]int)) == 1 {
91+
} else if len(s.args.([]int)) == 1 {
5492
//fmt.Println("idx ----------------3")
55-
obj, err = get_idx(obj, args.([]int)[0])
93+
obj, err = get_idx(obj, s.args.([]int)[0])
5694
if err != nil {
5795
return nil, err
5896
}
@@ -61,11 +99,11 @@ func JsonPathLookup(obj interface{}, jpath string) (interface{}, error) {
6199
return nil, fmt.Errorf("cannot index on empty slice")
62100
}
63101
case "range":
64-
obj, err = get_key(obj, key)
102+
obj, err = get_key(obj, s.key)
65103
if err != nil {
66104
return nil, err
67105
}
68-
if argsv, ok := args.([2]interface{}); ok == true {
106+
if argsv, ok := s.args.([2]interface{}); ok == true {
69107
obj, err = get_range(obj, argsv[0], argsv[1])
70108
if err != nil {
71109
return nil, err
@@ -74,11 +112,11 @@ func JsonPathLookup(obj interface{}, jpath string) (interface{}, error) {
74112
return nil, fmt.Errorf("range args length should be 2")
75113
}
76114
case "filter":
77-
obj, err = get_key(obj, key)
115+
obj, err = get_key(obj, s.key)
78116
if err != nil {
79117
return nil, err
80118
}
81-
obj, err = get_filtered(obj, obj, args.(string))
119+
obj, err = get_filtered(obj, obj, s.args.(string))
82120
if err != nil {
83121
return nil, err
84122
}

jsonpath_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,22 @@ func Test_jsonpath_num_cmp(t *testing.T) {
983983

984984
}
985985

986+
func BenchmarkJsonPathLookupCompiled(b *testing.B) {
987+
c, err := Compile("$.store.book[0].price")
988+
if err != nil {
989+
b.Fatalf("%v", err)
990+
}
991+
for n := 0; n < b.N; n++ {
992+
res, err := c.Lookup(json_data)
993+
if res_v, ok := res.(float64); ok != true || res_v != 8.95 {
994+
b.Errorf("$.store.book[0].price should be 8.95")
995+
}
996+
if err != nil {
997+
b.Errorf("Unexpected error: %v", err)
998+
}
999+
}
1000+
}
1001+
9861002
func BenchmarkJsonPathLookup(b *testing.B) {
9871003
for n := 0; n < b.N; n++ {
9881004
res, err := JsonPathLookup(json_data, "$.store.book[0].price")

0 commit comments

Comments
 (0)