graphql.go

  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}