diff options
Diffstat (limited to 'vendor/google.golang.org/api/gensupport/json.go')
-rw-r--r-- | vendor/google.golang.org/api/gensupport/json.go | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/vendor/google.golang.org/api/gensupport/json.go b/vendor/google.golang.org/api/gensupport/json.go new file mode 100644 index 0000000..c01e321 --- /dev/null +++ b/vendor/google.golang.org/api/gensupport/json.go | |||
@@ -0,0 +1,211 @@ | |||
1 | // Copyright 2015 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package gensupport | ||
6 | |||
7 | import ( | ||
8 | "encoding/json" | ||
9 | "fmt" | ||
10 | "reflect" | ||
11 | "strings" | ||
12 | ) | ||
13 | |||
14 | // MarshalJSON returns a JSON encoding of schema containing only selected fields. | ||
15 | // A field is selected if any of the following is true: | ||
16 | // * it has a non-empty value | ||
17 | // * its field name is present in forceSendFields and it is not a nil pointer or nil interface | ||
18 | // * its field name is present in nullFields. | ||
19 | // The JSON key for each selected field is taken from the field's json: struct tag. | ||
20 | func MarshalJSON(schema interface{}, forceSendFields, nullFields []string) ([]byte, error) { | ||
21 | if len(forceSendFields) == 0 && len(nullFields) == 0 { | ||
22 | return json.Marshal(schema) | ||
23 | } | ||
24 | |||
25 | mustInclude := make(map[string]bool) | ||
26 | for _, f := range forceSendFields { | ||
27 | mustInclude[f] = true | ||
28 | } | ||
29 | useNull := make(map[string]bool) | ||
30 | useNullMaps := make(map[string]map[string]bool) | ||
31 | for _, nf := range nullFields { | ||
32 | parts := strings.SplitN(nf, ".", 2) | ||
33 | field := parts[0] | ||
34 | if len(parts) == 1 { | ||
35 | useNull[field] = true | ||
36 | } else { | ||
37 | if useNullMaps[field] == nil { | ||
38 | useNullMaps[field] = map[string]bool{} | ||
39 | } | ||
40 | useNullMaps[field][parts[1]] = true | ||
41 | } | ||
42 | } | ||
43 | |||
44 | dataMap, err := schemaToMap(schema, mustInclude, useNull, useNullMaps) | ||
45 | if err != nil { | ||
46 | return nil, err | ||
47 | } | ||
48 | return json.Marshal(dataMap) | ||
49 | } | ||
50 | |||
51 | func schemaToMap(schema interface{}, mustInclude, useNull map[string]bool, useNullMaps map[string]map[string]bool) (map[string]interface{}, error) { | ||
52 | m := make(map[string]interface{}) | ||
53 | s := reflect.ValueOf(schema) | ||
54 | st := s.Type() | ||
55 | |||
56 | for i := 0; i < s.NumField(); i++ { | ||
57 | jsonTag := st.Field(i).Tag.Get("json") | ||
58 | if jsonTag == "" { | ||
59 | continue | ||
60 | } | ||
61 | tag, err := parseJSONTag(jsonTag) | ||
62 | if err != nil { | ||
63 | return nil, err | ||
64 | } | ||
65 | if tag.ignore { | ||
66 | continue | ||
67 | } | ||
68 | |||
69 | v := s.Field(i) | ||
70 | f := st.Field(i) | ||
71 | |||
72 | if useNull[f.Name] { | ||
73 | if !isEmptyValue(v) { | ||
74 | return nil, fmt.Errorf("field %q in NullFields has non-empty value", f.Name) | ||
75 | } | ||
76 | m[tag.apiName] = nil | ||
77 | continue | ||
78 | } | ||
79 | |||
80 | if !includeField(v, f, mustInclude) { | ||
81 | continue | ||
82 | } | ||
83 | |||
84 | // If map fields are explicitly set to null, use a map[string]interface{}. | ||
85 | if f.Type.Kind() == reflect.Map && useNullMaps[f.Name] != nil { | ||
86 | ms, ok := v.Interface().(map[string]string) | ||
87 | if !ok { | ||
88 | return nil, fmt.Errorf("field %q has keys in NullFields but is not a map[string]string", f.Name) | ||
89 | } | ||
90 | mi := map[string]interface{}{} | ||
91 | for k, v := range ms { | ||
92 | mi[k] = v | ||
93 | } | ||
94 | for k := range useNullMaps[f.Name] { | ||
95 | mi[k] = nil | ||
96 | } | ||
97 | m[tag.apiName] = mi | ||
98 | continue | ||
99 | } | ||
100 | |||
101 | // nil maps are treated as empty maps. | ||
102 | if f.Type.Kind() == reflect.Map && v.IsNil() { | ||
103 | m[tag.apiName] = map[string]string{} | ||
104 | continue | ||
105 | } | ||
106 | |||
107 | // nil slices are treated as empty slices. | ||
108 | if f.Type.Kind() == reflect.Slice && v.IsNil() { | ||
109 | m[tag.apiName] = []bool{} | ||
110 | continue | ||
111 | } | ||
112 | |||
113 | if tag.stringFormat { | ||
114 | m[tag.apiName] = formatAsString(v, f.Type.Kind()) | ||
115 | } else { | ||
116 | m[tag.apiName] = v.Interface() | ||
117 | } | ||
118 | } | ||
119 | return m, nil | ||
120 | } | ||
121 | |||
122 | // formatAsString returns a string representation of v, dereferencing it first if possible. | ||
123 | func formatAsString(v reflect.Value, kind reflect.Kind) string { | ||
124 | if kind == reflect.Ptr && !v.IsNil() { | ||
125 | v = v.Elem() | ||
126 | } | ||
127 | |||
128 | return fmt.Sprintf("%v", v.Interface()) | ||
129 | } | ||
130 | |||
131 | // jsonTag represents a restricted version of the struct tag format used by encoding/json. | ||
132 | // It is used to describe the JSON encoding of fields in a Schema struct. | ||
133 | type jsonTag struct { | ||
134 | apiName string | ||
135 | stringFormat bool | ||
136 | ignore bool | ||
137 | } | ||
138 | |||
139 | // parseJSONTag parses a restricted version of the struct tag format used by encoding/json. | ||
140 | // The format of the tag must match that generated by the Schema.writeSchemaStruct method | ||
141 | // in the api generator. | ||
142 | func parseJSONTag(val string) (jsonTag, error) { | ||
143 | if val == "-" { | ||
144 | return jsonTag{ignore: true}, nil | ||
145 | } | ||
146 | |||
147 | var tag jsonTag | ||
148 | |||
149 | i := strings.Index(val, ",") | ||
150 | if i == -1 || val[:i] == "" { | ||
151 | return tag, fmt.Errorf("malformed json tag: %s", val) | ||
152 | } | ||
153 | |||
154 | tag = jsonTag{ | ||
155 | apiName: val[:i], | ||
156 | } | ||
157 | |||
158 | switch val[i+1:] { | ||
159 | case "omitempty": | ||
160 | case "omitempty,string": | ||
161 | tag.stringFormat = true | ||
162 | default: | ||
163 | return tag, fmt.Errorf("malformed json tag: %s", val) | ||
164 | } | ||
165 | |||
166 | return tag, nil | ||
167 | } | ||
168 | |||
169 | // Reports whether the struct field "f" with value "v" should be included in JSON output. | ||
170 | func includeField(v reflect.Value, f reflect.StructField, mustInclude map[string]bool) bool { | ||
171 | // The regular JSON encoding of a nil pointer is "null", which means "delete this field". | ||
172 | // Therefore, we could enable field deletion by honoring pointer fields' presence in the mustInclude set. | ||
173 | // However, many fields are not pointers, so there would be no way to delete these fields. | ||
174 | // Rather than partially supporting field deletion, we ignore mustInclude for nil pointer fields. | ||
175 | // Deletion will be handled by a separate mechanism. | ||
176 | if f.Type.Kind() == reflect.Ptr && v.IsNil() { | ||
177 | return false | ||
178 | } | ||
179 | |||
180 | // The "any" type is represented as an interface{}. If this interface | ||
181 | // is nil, there is no reasonable representation to send. We ignore | ||
182 | // these fields, for the same reasons as given above for pointers. | ||
183 | if f.Type.Kind() == reflect.Interface && v.IsNil() { | ||
184 | return false | ||
185 | } | ||
186 | |||
187 | return mustInclude[f.Name] || !isEmptyValue(v) | ||
188 | } | ||
189 | |||
190 | // isEmptyValue reports whether v is the empty value for its type. This | ||
191 | // implementation is based on that of the encoding/json package, but its | ||
192 | // correctness does not depend on it being identical. What's important is that | ||
193 | // this function return false in situations where v should not be sent as part | ||
194 | // of a PATCH operation. | ||
195 | func isEmptyValue(v reflect.Value) bool { | ||
196 | switch v.Kind() { | ||
197 | case reflect.Array, reflect.Map, reflect.Slice, reflect.String: | ||
198 | return v.Len() == 0 | ||
199 | case reflect.Bool: | ||
200 | return !v.Bool() | ||
201 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
202 | return v.Int() == 0 | ||
203 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||
204 | return v.Uint() == 0 | ||
205 | case reflect.Float32, reflect.Float64: | ||
206 | return v.Float() == 0 | ||
207 | case reflect.Interface, reflect.Ptr: | ||
208 | return v.IsNil() | ||
209 | } | ||
210 | return false | ||
211 | } | ||