1 package jsonpath
2
3 import (
4 "bytes"
5 "context"
6 "fmt"
7 "strconv"
8 "text/scanner"
9
10 "github.com/PaesslerAG/gval"
11 )
12
13 type keyValueVisitor func(key string, value interface{})
14
15 type jsonObject interface {
16 visitElements(c context.Context, v interface{}, visit keyValueVisitor) error
17 }
18
19 type jsonObjectSlice []jsonObject
20
21 type keyValuePair struct {
22 key gval.Evaluable
23 value gval.Evaluable
24 }
25
26 type keyValueMatcher struct {
27 key gval.Evaluable
28 matcher func(c context.Context, r interface{}, visit pathMatcher)
29 }
30
31 func parseJSONObject(ctx context.Context, p *gval.Parser) (gval.Evaluable, error) {
32 evals := jsonObjectSlice{}
33 for {
34 switch p.Scan() {
35 default:
36 hasWildcard := false
37
38 p.Camouflage("object", ',', '}')
39 key, err := p.ParseExpression(context.WithValue(ctx, hasPlaceholdersContextKey{}, &hasWildcard))
40 if err != nil {
41 return nil, err
42 }
43 if p.Scan() != ':' {
44 if err != nil {
45 return nil, p.Expected("object", ':')
46 }
47 }
48 e, err := parseJSONObjectElement(ctx, p, hasWildcard, key)
49 if err != nil {
50 return nil, err
51 }
52 evals.addElements(e)
53 case ',':
54 case '}':
55 return evals.evaluable, nil
56 }
57 }
58 }
59
60 func parseJSONObjectElement(ctx context.Context, gParser *gval.Parser, hasWildcard bool, key gval.Evaluable) (jsonObject, error) {
61 if hasWildcard {
62 p := newParser(gParser)
63 switch gParser.Scan() {
64 case '$':
65 case '@':
66 p.appendPlainSelector(currentElementSelector())
67 default:
68 return nil, p.Expected("JSONPath key and value")
69 }
70
71 if err := p.parsePath(ctx); err != nil {
72 return nil, err
73 }
74 return keyValueMatcher{key, p.path.visitMatchs}, nil
75 }
76 value, err := gParser.ParseExpression(ctx)
77 if err != nil {
78 return nil, err
79 }
80 return keyValuePair{key, value}, nil
81 }
82
83 func (kv keyValuePair) visitElements(c context.Context, v interface{}, visit keyValueVisitor) error {
84 value, err := kv.value(c, v)
85 if err != nil {
86 return err
87 }
88 key, err := kv.key.EvalString(c, v)
89 if err != nil {
90 return err
91 }
92 visit(key, value)
93 return nil
94 }
95
96 func (kv keyValueMatcher) visitElements(c context.Context, v interface{}, visit keyValueVisitor) (err error) {
97 kv.matcher(c, v, func(keys []interface{}, match interface{}) {
98 key, er := kv.key.EvalString(context.WithValue(c, placeholdersContextKey{}, keys), v)
99 if er != nil {
100 err = er
101 }
102 visit(key, match)
103 })
104 return
105 }
106
107 func (j *jsonObjectSlice) addElements(e jsonObject) {
108 *j = append(*j, e)
109 }
110
111 func (j jsonObjectSlice) evaluable(c context.Context, v interface{}) (interface{}, error) {
112 vs := map[string]interface{}{}
113
114 err := j.visitElements(c, v, func(key string, value interface{}) { vs[key] = value })
115 if err != nil {
116 return nil, err
117 }
118 return vs, nil
119 }
120
121 func (j jsonObjectSlice) visitElements(ctx context.Context, v interface{}, visit keyValueVisitor) (err error) {
122 for _, e := range j {
123 if err := e.visitElements(ctx, v, visit); err != nil {
124 return err
125 }
126 }
127 return nil
128 }
129
130 func parsePlaceholder(c context.Context, p *gval.Parser) (gval.Evaluable, error) {
131 hasWildcard := c.Value(hasPlaceholdersContextKey{})
132 if hasWildcard == nil {
133 return nil, fmt.Errorf("JSONPath placeholder must only be used in an JSON object key")
134 }
135 *(hasWildcard.(*bool)) = true
136 switch p.Scan() {
137 case scanner.Int:
138 id, err := strconv.Atoi(p.TokenText())
139 if err != nil {
140 return nil, err
141 }
142 return placeholder(id).evaluable, nil
143 default:
144 p.Camouflage("JSONPath placeholder")
145 return allPlaceholders.evaluable, nil
146 }
147 }
148
149 type hasPlaceholdersContextKey struct{}
150
151 type placeholdersContextKey struct{}
152
153 type placeholder int
154
155 const allPlaceholders = placeholder(-1)
156
157 func (key placeholder) evaluable(c context.Context, v interface{}) (interface{}, error) {
158 wildcards, ok := c.Value(placeholdersContextKey{}).([]interface{})
159 if !ok || len(wildcards) <= int(key) {
160 return nil, fmt.Errorf("JSONPath placeholder #%d is not available", key)
161 }
162 if key == allPlaceholders {
163 sb := bytes.Buffer{}
164 sb.WriteString("$")
165 quoteWildcardValues(&sb, wildcards)
166 return sb.String(), nil
167 }
168 return wildcards[int(key)], nil
169 }
170
171 func quoteWildcardValues(sb *bytes.Buffer, wildcards []interface{}) {
172 for _, w := range wildcards {
173 if wildcards, ok := w.([]interface{}); ok {
174 quoteWildcardValues(sb, wildcards)
175 continue
176 }
177 sb.WriteString(fmt.Sprintf("[%v]",
178 strconv.Quote(fmt.Sprint(w)),
179 ))
180 }
181 }
182
View as plain text