func readObjectField(
	r FieldReader,
	addr []string,
	schema map[string]*Schema) (FieldReadResult, error) {
	result := make(map[string]interface{})
	exists := false
	for field, s := range schema {
		addrRead := make([]string, len(addr), len(addr)+1)
		copy(addrRead, addr)
		addrRead = append(addrRead, field)
		rawResult, err := r.ReadField(addrRead)
		if err != nil {
			return FieldReadResult{}, err
		}
		if rawResult.Exists {
			exists = true
		}
		result[field] = rawResult.ValueOrZero(s)
	}
	return FieldReadResult{
		Value:  result,
		Exists: exists,
	}, nil
}
readObjectField is a generic method for reading objects out of FieldReaders
based on the assumption that building an address of []string{k, FIELD}
will result in the proper field data.
package schema
import (
	"fmt"
	"strconv"
)
type FieldReader interface {
	ReadField([]string) (FieldReadResult, error)
}
type FieldReadResult struct {
	Value          interface{}
	ValueProcessed interface{}
	Exists bool
	Computed bool
}
func (r *FieldReadResult) ValueOrZero(s *Schema) interface{} {
	if r.Value != nil {
		return r.Value
	}
	return s.ZeroValue()
}
func addrToSchema(addr []string, schemaMap map[string]*Schema) []*Schema {
	current := &Schema{
		Type: typeObject,
		Elem: schemaMap,
	}
	if len(addr) == 0 {
		return []*Schema{current}
	}
	result := make([]*Schema, 0, len(addr))
	for len(addr) > 0 {
		k := addr[0]
		addr = addr[1:]
	REPEAT:
		if len(result) > 0 || current.Type != typeObject {
			result = append(result, current)
		}
		switch t := current.Type; t {
		case TypeBool, TypeInt, TypeFloat, TypeString:
			if len(addr) > 0 {
				return nil
			}
		case TypeList, TypeSet:
			isIndex := len(addr) > 0 && addr[0] == "#"
			switch v := current.Elem.(type) {
			case *Resource:
				current = &Schema{
					Type: typeObject,
					Elem: v.Schema,
				}
			case *Schema:
				current = v
			case ValueType:
				current = &Schema{Type: v}
			default:
				if isIndex {
					break
				}
				if len(addr) == 0 {
					return result
				}
				if len(addr) == 1 {
					if _, err := strconv.Atoi(addr[0]); err == nil {
						current = &Schema{Type: TypeString}
						break
					}
				}
				return nil
			}
			if isIndex {
				current = &Schema{Type: TypeInt}
				break
			}
		case TypeMap:
			if len(addr) > 0 {
				switch v := current.Elem.(type) {
				case ValueType:
					current = &Schema{Type: v}
				default:
					current = &Schema{Type: TypeString}
				}
			}
		case typeObject:
			if len(result) > 0 {
				lastType := result[len(result)-2].Type
				if lastType == TypeSet || lastType == TypeList {
					if len(addr) == 0 {
						break
					}
					k = addr[0]
					addr = addr[1:]
				}
			}
			m := current.Elem.(map[string]*Schema)
			val, ok := m[k]
			if !ok {
				return nil
			}
			current = val
			goto REPEAT
		}
	}
	return result
}
func readListField(
	r FieldReader, addr []string, schema *Schema) (FieldReadResult, error) {
	addrPadded := make([]string, len(addr)+1)
	copy(addrPadded, addr)
	addrPadded[len(addrPadded)-1] = "#"
	countResult, err := r.ReadField(addrPadded)
	if err != nil {
		return FieldReadResult{}, err
	}
	if !countResult.Exists {
		countResult.Value = 0
	}
	if countResult.Computed || countResult.Value.(int) == 0 {
		return FieldReadResult{
			Value:    []interface{}{},
			Exists:   countResult.Exists,
			Computed: countResult.Computed,
		}, nil
	}
	result := make([]interface{}, countResult.Value.(int))
	for i, _ := range result {
		is := strconv.FormatInt(int64(i), 10)
		addrPadded[len(addrPadded)-1] = is
		rawResult, err := r.ReadField(addrPadded)
		if err != nil {
			return FieldReadResult{}, err
		}
		if !rawResult.Exists {
			rawResult.Value = nil
		}
		result[i] = rawResult.Value
	}
	return FieldReadResult{
		Value:  result,
		Exists: true,
	}, nil
}