1
16
17 package json
18
19 import (
20 "bytes"
21 gojson "encoding/json"
22 "io/ioutil"
23 "reflect"
24 "strings"
25 "testing"
26 )
27
28 func TestSyntaxErrorOffset(t *testing.T) {
29 malformedJSON :=
30 []byte(`{
31 "test1":true,
32 "test2":true
33 "test3":true
34 }`)
35
36 err1 := UnmarshalCaseSensitivePreserveInts(malformedJSON, &map[string]interface{}{})
37 if err1 == nil {
38 t.Fatal("expected err, got none")
39 }
40 ok1, offset1 := SyntaxErrorOffset(err1)
41 if !ok1 {
42 t.Fatal("expected ok, got false")
43 }
44
45 err2 := gojson.Unmarshal(malformedJSON, &map[string]interface{}{})
46 if err2 == nil {
47 t.Fatal("expected err, got none")
48 }
49 ok2, offset2 := SyntaxErrorOffset(err2)
50 if !ok2 {
51 t.Fatal("expected ok, got false")
52 }
53 if offset1 != offset2 {
54 t.Fatalf("offset mismatch from stdlib and custom: %d != %d", offset1, offset2)
55 }
56 }
57
58 func TestUnmarshal(t *testing.T) {
59 type Obj struct {
60 A int `json:"a"`
61 B int `json:"b"`
62 C map[string]string `json:"c"`
63 D int
64 E int
65 }
66
67 testcases := []struct {
68 name string
69 in string
70 to func() interface{}
71 expect interface{}
72 expectErr string
73 expectStrictErrs []string
74 }{
75 {
76 name: "simple",
77 in: `{"a":1}`,
78 to: func() interface{} { return map[string]interface{}{} },
79 expect: map[string]interface{}{"a": int64(1)},
80 },
81 {
82 name: "case-sensitive",
83 in: `{"a":1,"A":2,"B":3}`,
84 to: func() interface{} { return &Obj{} },
85 expect: &Obj{A: 1},
86 expectStrictErrs: []string{`unknown field "A"`, `unknown field "B"`},
87 },
88 {
89 name: "duplicate untyped",
90 in: `{"a":1,"a":2,"b":1,"b":2}`,
91 to: func() interface{} { return map[string]interface{}{} },
92 expect: map[string]interface{}{"a": int64(2), "b": int64(2)},
93 expectStrictErrs: []string{`duplicate field "a"`, `duplicate field "b"`},
94 },
95 {
96 name: "duplicate typed",
97 in: `{"a":1,"a":2,"b":1,"b":2}`,
98 to: func() interface{} { return &Obj{} },
99 expect: &Obj{A: 2, B: 2},
100 expectStrictErrs: []string{`duplicate field "a"`, `duplicate field "b"`},
101 },
102 {
103 name: "duplicate map field",
104 in: `{"c":{"a":"1","a":"2","b":"1","b":"2"}}`,
105 to: func() interface{} { return &Obj{} },
106 expect: &Obj{C: map[string]string{"a": "2", "b": "2"}},
107 expectStrictErrs: []string{`duplicate field "c.a"`, `duplicate field "c.b"`},
108 },
109 {
110 name: "unknown fields",
111 in: `{"a":1,"unknown":true,"unknown2":false,"b":2}`,
112 to: func() interface{} { return &Obj{} },
113 expect: &Obj{A: 1, B: 2},
114 expectStrictErrs: []string{`unknown field "unknown"`, `unknown field "unknown2"`},
115 },
116 }
117
118 for _, tc := range testcases {
119 t.Run(tc.name, func(t *testing.T) {
120 unmarshalTo := tc.to()
121 err := UnmarshalCaseSensitivePreserveInts([]byte(tc.in), &unmarshalTo)
122
123 strictUnmarshalTo := tc.to()
124 strictErrors, strictErr := UnmarshalStrict([]byte(tc.in), &strictUnmarshalTo)
125
126 decodeTo := tc.to()
127 decodeErr := NewDecoderCaseSensitivePreserveInts(bytes.NewBuffer([]byte(tc.in))).Decode(&decodeTo)
128
129
130 if (len(tc.expectErr) > 0) != (err != nil) {
131 t.Fatalf("expected err=%v, got %v", len(tc.expectErr) > 0, err)
132 }
133 if len(tc.expectErr) > 0 && !strings.Contains(err.Error(), tc.expectErr) {
134 t.Fatalf("expected error containing '%s', got %v", tc.expectErr, err)
135 }
136
137
138 if len(tc.expectStrictErrs) != len(strictErrors) {
139 t.Fatalf("expected %d strict errors, got %v", len(tc.expectStrictErrs), strictErrors)
140 }
141 for i := range tc.expectStrictErrs {
142 strictFieldErr, ok := strictErrors[i].(FieldError)
143 if !ok {
144 t.Fatalf("strict error does not implement FieldError: %v", strictErrors[i])
145 }
146 if !strings.Contains(strictFieldErr.Error(), tc.expectStrictErrs[i]) {
147 t.Fatalf("expected strict errors:\n %s\ngot:\n %v", strings.Join(tc.expectStrictErrs, "\n "), strictErrors)
148 }
149 }
150
151
152 if (len(tc.expectErr) > 0) != (decodeErr != nil) {
153 t.Fatalf("expected err=%v, got %v", len(tc.expectErr) > 0, decodeErr)
154 }
155 if len(tc.expectErr) > 0 && !strings.Contains(decodeErr.Error(), tc.expectErr) {
156 t.Fatalf("expected error containing '%s', got %v", tc.expectErr, decodeErr)
157 }
158
159
160 if !reflect.DeepEqual(tc.expect, unmarshalTo) {
161 t.Fatalf("expected\n%#v\ngot\n%#v", tc.expect, unmarshalTo)
162 }
163 if !reflect.DeepEqual(tc.expect, decodeTo) {
164 t.Fatalf("expected\n%#v\ngot\n%#v", tc.expect, decodeTo)
165 }
166
167
168 if !reflect.DeepEqual(err, strictErr) {
169 t.Fatalf("unmarshal/strictunmarshal returned different errors:\n%v\n%v", err, strictErr)
170 }
171 if !reflect.DeepEqual(unmarshalTo, strictUnmarshalTo) {
172 t.Fatalf("unmarshal/strictunmarshal returned different objects:\n%#v\n%#v", unmarshalTo, strictUnmarshalTo)
173 }
174
175
176 if !reflect.DeepEqual(err, decodeErr) {
177 t.Fatalf("unmarshal/decode returned different errors:\n%v\n%v", err, decodeErr)
178 }
179 if !reflect.DeepEqual(unmarshalTo, decodeTo) {
180 t.Fatalf("unmarshal/decode returned different objects:\n%#v\n%#v", unmarshalTo, decodeTo)
181 }
182 })
183 }
184 }
185
186 func BenchmarkUnmarshal(b *testing.B) {
187 testcases := []struct {
188 name string
189 unmarshal func(b *testing.B, data []byte, v interface{})
190 }{
191 {
192 name: "stdlib",
193 unmarshal: func(b *testing.B, data []byte, v interface{}) {
194 if err := gojson.Unmarshal(data, v); err != nil {
195 b.Fatal(err)
196 }
197 },
198 },
199 {
200 name: "unmarshal",
201 unmarshal: func(b *testing.B, data []byte, v interface{}) {
202 if err := UnmarshalCaseSensitivePreserveInts(data, v); err != nil {
203 b.Fatal(err)
204 }
205 },
206 },
207 {
208 name: "strict",
209 unmarshal: func(b *testing.B, data []byte, v interface{}) {
210 if strict, err := UnmarshalStrict(data, v); err != nil {
211 b.Fatal(err)
212 } else if len(strict) > 0 {
213 b.Fatal(strict)
214 }
215 },
216 },
217 {
218 name: "strict-custom",
219 unmarshal: func(b *testing.B, data []byte, v interface{}) {
220 if strict, err := UnmarshalStrict(data, v, DisallowDuplicateFields, DisallowUnknownFields); err != nil {
221 b.Fatal(err)
222 } else if len(strict) > 0 {
223 b.Fatal(strict)
224 }
225 },
226 },
227 }
228
229 data, err := ioutil.ReadFile("testdata/bench.json")
230 if err != nil {
231 b.Fatal(err)
232 }
233 b.ResetTimer()
234
235 for _, tc := range testcases {
236 b.Run("typed_"+tc.name, func(b *testing.B) {
237 for i := 0; i < b.N; i++ {
238 tc.unmarshal(b, data, &A{})
239 }
240 })
241 }
242 for _, tc := range testcases {
243 b.Run("untyped_"+tc.name, func(b *testing.B) {
244 for i := 0; i < b.N; i++ {
245 tc.unmarshal(b, data, &map[string]interface{}{})
246 }
247 })
248 }
249 }
250
251 type A struct {
252 Int int `json:"int"`
253 Bool bool `json:"bool"`
254 String string `json:"string"`
255
256 StringMap map[string]string `json:"map"`
257 ObjectArray []A `json:"array"`
258
259 Small Small `json:"small"`
260 Big Big `json:"big"`
261
262 Custom Custom `json:"custom"`
263 }
264
265 type Small struct {
266 F01 string `json:"f01"`
267 F02 string `json:"f02"`
268 F03 string `json:"f03"`
269 F04 string `json:"f04"`
270 F05 string `json:"f05"`
271 F06 string `json:"f06"`
272 F07 string `json:"f07"`
273 F08 string `json:"f08"`
274 F09 string `json:"f09"`
275 F10 string `json:"f10"`
276 F11 string `json:"f11"`
277 F12 string `json:"f12"`
278 F13 string `json:"f13"`
279 F14 string `json:"f14"`
280 F15 string `json:"f15"`
281 F16 string `json:"f16"`
282 F17 string `json:"f17"`
283 F18 string `json:"f18"`
284 F19 string `json:"f19"`
285 F20 string `json:"f20"`
286 F21 string `json:"f21"`
287 F22 string `json:"f22"`
288 F23 string `json:"f23"`
289 F24 string `json:"f24"`
290 F25 string `json:"f25"`
291 F26 string `json:"f26"`
292 F27 string `json:"f27"`
293 F28 string `json:"f28"`
294 F29 string `json:"f29"`
295 F30 string `json:"f30"`
296 F31 string `json:"f31"`
297 F32 string `json:"f32"`
298 F33 string `json:"f33"`
299 F34 string `json:"f34"`
300 F35 string `json:"f35"`
301 F36 string `json:"f36"`
302 F37 string `json:"f37"`
303 F38 string `json:"f38"`
304 F39 string `json:"f39"`
305 F40 string `json:"f40"`
306 F41 string `json:"f41"`
307 F42 string `json:"f42"`
308 F43 string `json:"f43"`
309 F44 string `json:"f44"`
310 F45 string `json:"f45"`
311 F46 string `json:"f46"`
312 F47 string `json:"f47"`
313 F48 string `json:"f48"`
314 F49 string `json:"f49"`
315 F50 string `json:"f50"`
316 F51 string `json:"f51"`
317 F52 string `json:"f52"`
318 F53 string `json:"f53"`
319 F54 string `json:"f54"`
320 F55 string `json:"f55"`
321 F56 string `json:"f56"`
322 F57 string `json:"f57"`
323 F58 string `json:"f58"`
324 F59 string `json:"f59"`
325 F60 string `json:"f60"`
326 F61 string `json:"f61"`
327 F62 string `json:"f62"`
328 F63 string `json:"f63"`
329 F64 string `json:"f64"`
330 }
331
332 type Big struct {
333 F01 string `json:"f01"`
334 F02 string `json:"f02"`
335 F03 string `json:"f03"`
336 F04 string `json:"f04"`
337 F05 string `json:"f05"`
338 F06 string `json:"f06"`
339 F07 string `json:"f07"`
340 F08 string `json:"f08"`
341 F09 string `json:"f09"`
342 F10 string `json:"f10"`
343 F11 string `json:"f11"`
344 F12 string `json:"f12"`
345 F13 string `json:"f13"`
346 F14 string `json:"f14"`
347 F15 string `json:"f15"`
348 F16 string `json:"f16"`
349 F17 string `json:"f17"`
350 F18 string `json:"f18"`
351 F19 string `json:"f19"`
352 F20 string `json:"f20"`
353 F21 string `json:"f21"`
354 F22 string `json:"f22"`
355 F23 string `json:"f23"`
356 F24 string `json:"f24"`
357 F25 string `json:"f25"`
358 F26 string `json:"f26"`
359 F27 string `json:"f27"`
360 F28 string `json:"f28"`
361 F29 string `json:"f29"`
362 F30 string `json:"f30"`
363 F31 string `json:"f31"`
364 F32 string `json:"f32"`
365 F33 string `json:"f33"`
366 F34 string `json:"f34"`
367 F35 string `json:"f35"`
368 F36 string `json:"f36"`
369 F37 string `json:"f37"`
370 F38 string `json:"f38"`
371 F39 string `json:"f39"`
372 F40 string `json:"f40"`
373 F41 string `json:"f41"`
374 F42 string `json:"f42"`
375 F43 string `json:"f43"`
376 F44 string `json:"f44"`
377 F45 string `json:"f45"`
378 F46 string `json:"f46"`
379 F47 string `json:"f47"`
380 F48 string `json:"f48"`
381 F49 string `json:"f49"`
382 F50 string `json:"f50"`
383 F51 string `json:"f51"`
384 F52 string `json:"f52"`
385 F53 string `json:"f53"`
386 F54 string `json:"f54"`
387 F55 string `json:"f55"`
388 F56 string `json:"f56"`
389 F57 string `json:"f57"`
390 F58 string `json:"f58"`
391 F59 string `json:"f59"`
392 F60 string `json:"f60"`
393 F61 string `json:"f61"`
394 F62 string `json:"f62"`
395 F63 string `json:"f63"`
396 F64 string `json:"f64"`
397 F65 string `json:"f65"`
398 }
399
400 type Custom struct{}
401
402 func (c *Custom) UnmarshalJSON(data []byte) error {
403 return nil
404 }
405
View as plain text