1// Package jsonutil provides a function for decoding JSON
2// into a GraphQL query data structure.
3package jsonutil
4
5import (
6 "bytes"
7 "encoding/json"
8 "errors"
9 "fmt"
10 "io"
11 "reflect"
12 "strings"
13)
14
15// UnmarshalGraphQL parses the JSON-encoded GraphQL response data and stores
16// the result in the GraphQL query data structure pointed to by v.
17//
18// The implementation is created on top of the JSON tokenizer available
19// in "encoding/json".Decoder.
20func UnmarshalGraphQL(data []byte, v interface{}) error {
21 dec := json.NewDecoder(bytes.NewReader(data))
22 dec.UseNumber()
23 err := (&decoder{tokenizer: dec}).Decode(v)
24 if err != nil {
25 return err
26 }
27 tok, err := dec.Token()
28 switch err {
29 case io.EOF:
30 // Expect to get io.EOF. There shouldn't be any more
31 // tokens left after we've decoded v successfully.
32 return nil
33 case nil:
34 return fmt.Errorf("invalid token '%v' after top-level value", tok)
35 default:
36 return err
37 }
38}
39
40// decoder is a JSON decoder that performs custom unmarshaling behavior
41// for GraphQL query data structures. It's implemented on top of a JSON tokenizer.
42type decoder struct {
43 tokenizer interface {
44 Token() (json.Token, error)
45 }
46
47 // Stack of what part of input JSON we're in the middle of - objects, arrays.
48 parseState []json.Delim
49
50 // Stacks of values where to unmarshal.
51 // The top of each stack is the reflect.Value where to unmarshal next JSON value.
52 //
53 // The reason there's more than one stack is because we might be unmarshaling
54 // a single JSON value into multiple GraphQL fragments or embedded structs, so
55 // we keep track of them all.
56 vs [][]reflect.Value
57}
58
59// Decode decodes a single JSON value from d.tokenizer into v.
60func (d *decoder) Decode(v interface{}) error {
61 rv := reflect.ValueOf(v)
62 if rv.Kind() != reflect.Ptr {
63 return fmt.Errorf("cannot decode into non-pointer %T", v)
64 }
65 d.vs = [][]reflect.Value{{rv.Elem()}}
66 return d.decode()
67}
68
69// decode decodes a single JSON value from d.tokenizer into d.vs.
70func (d *decoder) decode() error {
71 // The loop invariant is that the top of each d.vs stack
72 // is where we try to unmarshal the next JSON value we see.
73 for len(d.vs) > 0 {
74 tok, err := d.tokenizer.Token()
75 if err == io.EOF {
76 return errors.New("unexpected end of JSON input")
77 } else if err != nil {
78 return err
79 }
80
81 switch {
82
83 // Are we inside an object and seeing next key (rather than end of object)?
84 case d.state() == '{' && tok != json.Delim('}'):
85 key, ok := tok.(string)
86 if !ok {
87 return errors.New("unexpected non-key in JSON input")
88 }
89 someFieldExist := false
90 for i := range d.vs {
91 v := d.vs[i][len(d.vs[i])-1]
92 if v.Kind() == reflect.Ptr {
93 v = v.Elem()
94 }
95 var f reflect.Value
96 if v.Kind() == reflect.Struct {
97 f = fieldByGraphQLName(v, key)
98 if f.IsValid() {
99 someFieldExist = true
100 }
101 }
102 d.vs[i] = append(d.vs[i], f)
103 }
104 if !someFieldExist {
105 return fmt.Errorf("struct field for %s doesn't exist in any of %v places to unmarshal", key, len(d.vs))
106 }
107
108 // We've just consumed the current token, which was the key.
109 // Read the next token, which should be the value, and let the rest of code process it.
110 tok, err = d.tokenizer.Token()
111 if err == io.EOF {
112 return errors.New("unexpected end of JSON input")
113 } else if err != nil {
114 return err
115 }
116
117 // Are we inside an array and seeing next value (rather than end of array)?
118 case d.state() == '[' && tok != json.Delim(']'):
119 someSliceExist := false
120 for i := range d.vs {
121 v := d.vs[i][len(d.vs[i])-1]
122 if v.Kind() == reflect.Ptr {
123 v = v.Elem()
124 }
125 var f reflect.Value
126 if v.Kind() == reflect.Slice {
127 v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem()))) // v = append(v, T).
128 f = v.Index(v.Len() - 1)
129 someSliceExist = true
130 }
131 d.vs[i] = append(d.vs[i], f)
132 }
133 if !someSliceExist {
134 return fmt.Errorf("slice doesn't exist in any of %v places to unmarshal", len(d.vs))
135 }
136 }
137
138 switch tok := tok.(type) {
139 case string, json.Number, bool, nil:
140 // Value.
141
142 for i := range d.vs {
143 v := d.vs[i][len(d.vs[i])-1]
144 if !v.IsValid() {
145 continue
146 }
147 err := unmarshalValue(tok, v)
148 if err != nil {
149 return err
150 }
151 }
152 d.popAllVs()
153
154 case json.Delim:
155 switch tok {
156 case '{':
157 // Start of object.
158
159 d.pushState(tok)
160
161 frontier := make([]reflect.Value, len(d.vs)) // Places to look for GraphQL fragments/embedded structs.
162 for i := range d.vs {
163 v := d.vs[i][len(d.vs[i])-1]
164 frontier[i] = v
165 // TODO: Do this recursively or not? Add a test case if needed.
166 if v.Kind() == reflect.Ptr && v.IsNil() {
167 v.Set(reflect.New(v.Type().Elem())) // v = new(T).
168 }
169 }
170 // Find GraphQL fragments/embedded structs recursively, adding to frontier
171 // as new ones are discovered and exploring them further.
172 for len(frontier) > 0 {
173 v := frontier[0]
174 frontier = frontier[1:]
175 if v.Kind() == reflect.Ptr {
176 v = v.Elem()
177 }
178 if v.Kind() != reflect.Struct {
179 continue
180 }
181 for i := 0; i < v.NumField(); i++ {
182 if isGraphQLFragment(v.Type().Field(i)) || v.Type().Field(i).Anonymous {
183 // Add GraphQL fragment or embedded struct.
184 d.vs = append(d.vs, []reflect.Value{v.Field(i)})
185 frontier = append(frontier, v.Field(i))
186 }
187 }
188 }
189 case '[':
190 // Start of array.
191
192 d.pushState(tok)
193
194 for i := range d.vs {
195 v := d.vs[i][len(d.vs[i])-1]
196 // TODO: Confirm this is needed, write a test case.
197 //if v.Kind() == reflect.Ptr && v.IsNil() {
198 // v.Set(reflect.New(v.Type().Elem())) // v = new(T).
199 //}
200
201 // Reset slice to empty (in case it had non-zero initial value).
202 if v.Kind() == reflect.Ptr {
203 v = v.Elem()
204 }
205 if v.Kind() != reflect.Slice {
206 continue
207 }
208 v.Set(reflect.MakeSlice(v.Type(), 0, 0)) // v = make(T, 0, 0).
209 }
210 case '}', ']':
211 // End of object or array.
212 d.popAllVs()
213 d.popState()
214 default:
215 return errors.New("unexpected delimiter in JSON input")
216 }
217 default:
218 return errors.New("unexpected token in JSON input")
219 }
220 }
221 return nil
222}
223
224// pushState pushes a new parse state s onto the stack.
225func (d *decoder) pushState(s json.Delim) {
226 d.parseState = append(d.parseState, s)
227}
228
229// popState pops a parse state (already obtained) off the stack.
230// The stack must be non-empty.
231func (d *decoder) popState() {
232 d.parseState = d.parseState[:len(d.parseState)-1]
233}
234
235// state reports the parse state on top of stack, or 0 if empty.
236func (d *decoder) state() json.Delim {
237 if len(d.parseState) == 0 {
238 return 0
239 }
240 return d.parseState[len(d.parseState)-1]
241}
242
243// popAllVs pops from all d.vs stacks, keeping only non-empty ones.
244func (d *decoder) popAllVs() {
245 var nonEmpty [][]reflect.Value
246 for i := range d.vs {
247 d.vs[i] = d.vs[i][:len(d.vs[i])-1]
248 if len(d.vs[i]) > 0 {
249 nonEmpty = append(nonEmpty, d.vs[i])
250 }
251 }
252 d.vs = nonEmpty
253}
254
255// fieldByGraphQLName returns a struct field of struct v that matches GraphQL name,
256// or invalid reflect.Value if none found.
257func fieldByGraphQLName(v reflect.Value, name string) reflect.Value {
258 for i := 0; i < v.NumField(); i++ {
259 if hasGraphQLName(v.Type().Field(i), name) {
260 return v.Field(i)
261 }
262 }
263 return reflect.Value{}
264}
265
266// hasGraphQLName reports whether struct field f has GraphQL name.
267func hasGraphQLName(f reflect.StructField, name string) bool {
268 value, ok := f.Tag.Lookup("graphql")
269 if !ok {
270 // TODO: caseconv package is relatively slow. Optimize it, then consider using it here.
271 //return caseconv.MixedCapsToLowerCamelCase(f.Name) == name
272 return strings.EqualFold(f.Name, name)
273 }
274 value = strings.TrimSpace(value) // TODO: Parse better.
275 if strings.HasPrefix(value, "...") {
276 // GraphQL fragment. It doesn't have a name.
277 return false
278 }
279 if i := strings.Index(value, "("); i != -1 {
280 value = value[:i]
281 }
282 if i := strings.Index(value, ":"); i != -1 {
283 value = value[:i]
284 }
285 return strings.TrimSpace(value) == name
286}
287
288// isGraphQLFragment reports whether struct field f is a GraphQL fragment.
289func isGraphQLFragment(f reflect.StructField) bool {
290 value, ok := f.Tag.Lookup("graphql")
291 if !ok {
292 return false
293 }
294 value = strings.TrimSpace(value) // TODO: Parse better.
295 return strings.HasPrefix(value, "...")
296}
297
298// unmarshalValue unmarshals JSON value into v.
299func unmarshalValue(value json.Token, v reflect.Value) error {
300 b, err := json.Marshal(value) // TODO: Short-circuit (if profiling says it's worth it).
301 if err != nil {
302 return err
303 }
304 if !v.CanAddr() {
305 return fmt.Errorf("value %v is not addressable", v)
306 }
307 return json.Unmarshal(b, v.Addr().Interface())
308}