1// Copyright 2010 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 5package json 6 7import ( 8 "bytes" 9 "io" 10 "log" 11 "net" 12 "net/http" 13 "net/http/httptest" 14 "reflect" 15 "strings" 16 "testing" 17) 18 19// Test values for the stream test. 20// One of each JSON kind. 21var streamTest = []interface{}{ 22 0.1, 23 "hello", 24 nil, 25 true, 26 false, 27 []interface{}{"a", "b", "c"}, 28 map[string]interface{}{"K": "Kelvin", "ß": "long s"}, 29 3.14, // another value to make sure something can follow map 30} 31 32var streamEncoded = `0.1 33"hello" 34null 35true 36false 37["a","b","c"] 38{"ß":"long s","K":"Kelvin"} 393.14 40` 41 42func TestEncoder(t *testing.T) { 43 for i := 0; i <= len(streamTest); i++ { 44 var buf bytes.Buffer 45 enc := NewEncoder(&buf) 46 // Check that enc.SetIndent("", "") turns off indentation. 47 enc.SetIndent(">", ".") 48 enc.SetIndent("", "") 49 for j, v := range streamTest[0:i] { 50 if err := enc.Encode(v); err != nil { 51 t.Fatalf("encode #%d: %v", j, err) 52 } 53 } 54 if have, want := buf.String(), nlines(streamEncoded, i); have != want { 55 t.Errorf("encoding %d items: mismatch", i) 56 diff(t, []byte(have), []byte(want)) 57 break 58 } 59 } 60} 61 62var streamEncodedIndent = `0.1 63"hello" 64null 65true 66false 67[ 68>."a", 69>."b", 70>."c" 71>] 72{ 73>."ß": "long s", 74>."K": "Kelvin" 75>} 763.14 77` 78 79func TestEncoderIndent(t *testing.T) { 80 var buf bytes.Buffer 81 enc := NewEncoder(&buf) 82 enc.SetIndent(">", ".") 83 for _, v := range streamTest { 84 enc.Encode(v) 85 } 86 if have, want := buf.String(), streamEncodedIndent; have != want { 87 t.Error("indented encoding mismatch") 88 diff(t, []byte(have), []byte(want)) 89 } 90} 91 92type strMarshaler string 93 94func (s strMarshaler) MarshalJSON() ([]byte, error) { 95 return []byte(s), nil 96} 97 98type strPtrMarshaler string 99 100func (s *strPtrMarshaler) MarshalJSON() ([]byte, error) { 101 return []byte(*s), nil 102} 103 104func TestEncoderSetEscapeHTML(t *testing.T) { 105 var c C 106 var ct CText 107 var tagStruct struct { 108 Valid int `json:"<>&#! "` 109 Invalid int `json:"\\"` 110 } 111 112 // This case is particularly interesting, as we force the encoder to 113 // take the address of the Ptr field to use its MarshalJSON method. This 114 // is why the '&' is important. 115 marshalerStruct := &struct { 116 NonPtr strMarshaler 117 Ptr strPtrMarshaler 118 }{`"<str>"`, `"<str>"`} 119 120 // https://golang.org/issue/34154 121 stringOption := struct { 122 Bar string `json:"bar,string"` 123 }{`<html>foobar</html>`} 124 125 for _, tt := range []struct { 126 name string 127 v interface{} 128 wantEscape string 129 want string 130 }{ 131 {"c", c, `"\u003c\u0026\u003e"`, `"<&>"`}, 132 {"ct", ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`}, 133 {`"<&>"`, "<&>", `"\u003c\u0026\u003e"`, `"<&>"`}, 134 { 135 "tagStruct", tagStruct, 136 `{"\u003c\u003e\u0026#! ":0,"Invalid":0}`, 137 `{"<>&#! ":0,"Invalid":0}`, 138 }, 139 { 140 `"<str>"`, marshalerStruct, 141 `{"NonPtr":"\u003cstr\u003e","Ptr":"\u003cstr\u003e"}`, 142 `{"NonPtr":"<str>","Ptr":"<str>"}`, 143 }, 144 { 145 "stringOption", stringOption, 146 `{"bar":"\"\\u003chtml\\u003efoobar\\u003c/html\\u003e\""}`, 147 `{"bar":"\"<html>foobar</html>\""}`, 148 }, 149 } { 150 var buf bytes.Buffer 151 enc := NewEncoder(&buf) 152 if err := enc.Encode(tt.v); err != nil { 153 t.Errorf("Encode(%s): %s", tt.name, err) 154 continue 155 } 156 if got := strings.TrimSpace(buf.String()); got != tt.wantEscape { 157 t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape) 158 } 159 buf.Reset() 160 enc.SetEscapeHTML(false) 161 if err := enc.Encode(tt.v); err != nil { 162 t.Errorf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err) 163 continue 164 } 165 if got := strings.TrimSpace(buf.String()); got != tt.want { 166 t.Errorf("SetEscapeHTML(false) Encode(%s) = %#q, want %#q", 167 tt.name, got, tt.want) 168 } 169 } 170} 171 172func TestDecoder(t *testing.T) { 173 for i := 0; i <= len(streamTest); i++ { 174 // Use stream without newlines as input, 175 // just to stress the decoder even more. 176 // Our test input does not include back-to-back numbers. 177 // Otherwise stripping the newlines would 178 // merge two adjacent JSON values. 179 var buf bytes.Buffer 180 for _, c := range nlines(streamEncoded, i) { 181 if c != '\n' { 182 buf.WriteRune(c) 183 } 184 } 185 out := make([]interface{}, i) 186 dec := NewDecoder(&buf) 187 for j := range out { 188 if err := dec.Decode(&out[j]); err != nil { 189 t.Fatalf("decode #%d/%d: %v", j, i, err) 190 } 191 } 192 if !reflect.DeepEqual(out, streamTest[0:i]) { 193 t.Errorf("decoding %d items: mismatch", i) 194 for j := range out { 195 if !reflect.DeepEqual(out[j], streamTest[j]) { 196 t.Errorf("#%d: have %v want %v", j, out[j], streamTest[j]) 197 } 198 } 199 break 200 } 201 } 202} 203 204func TestDecoderBuffered(t *testing.T) { 205 r := strings.NewReader(`{"Name": "Gopher"} extra `) 206 var m struct { 207 Name string 208 } 209 d := NewDecoder(r) 210 err := d.Decode(&m) 211 if err != nil { 212 t.Fatal(err) 213 } 214 if m.Name != "Gopher" { 215 t.Errorf("Name = %q; want Gopher", m.Name) 216 } 217 rest, err := io.ReadAll(d.Buffered()) 218 if err != nil { 219 t.Fatal(err) 220 } 221 if g, w := string(rest), " extra "; g != w { 222 t.Errorf("Remaining = %q; want %q", g, w) 223 } 224} 225 226func nlines(s string, n int) string { 227 if n <= 0 { 228 return "" 229 } 230 for i, c := range s { 231 if c == '\n' { 232 if n--; n == 0 { 233 return s[0 : i+1] 234 } 235 } 236 } 237 return s 238} 239 240func TestRawMessage(t *testing.T) { 241 var data struct { 242 X float64 243 Id RawMessage 244 Y float32 245 } 246 const raw = `["\u0056",null]` 247 const msg = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}` 248 err := Unmarshal([]byte(msg), &data) 249 if err != nil { 250 t.Fatalf("Unmarshal: %v", err) 251 } 252 if string([]byte(data.Id)) != raw { 253 t.Fatalf("Raw mismatch: have %#q want %#q", []byte(data.Id), raw) 254 } 255 b, err := Marshal(&data) 256 if err != nil { 257 t.Fatalf("Marshal: %v", err) 258 } 259 if string(b) != msg { 260 t.Fatalf("Marshal: have %#q want %#q", b, msg) 261 } 262} 263 264func TestNullRawMessage(t *testing.T) { 265 var data struct { 266 X float64 267 Id RawMessage 268 IdPtr *RawMessage 269 Y float32 270 } 271 const msg = `{"X":0.1,"Id":null,"IdPtr":null,"Y":0.2}` 272 err := Unmarshal([]byte(msg), &data) 273 if err != nil { 274 t.Fatalf("Unmarshal: %v", err) 275 } 276 if want, got := "null", string(data.Id); want != got { 277 t.Fatalf("Raw mismatch: have %q, want %q", got, want) 278 } 279 if data.IdPtr != nil { 280 t.Fatalf("Raw pointer mismatch: have non-nil, want nil") 281 } 282 b, err := Marshal(&data) 283 if err != nil { 284 t.Fatalf("Marshal: %v", err) 285 } 286 if string(b) != msg { 287 t.Fatalf("Marshal: have %#q want %#q", b, msg) 288 } 289} 290 291var blockingTests = []string{ 292 `{"x": 1}`, 293 `[1, 2, 3]`, 294} 295 296func TestBlocking(t *testing.T) { 297 for _, enc := range blockingTests { 298 r, w := net.Pipe() 299 go w.Write([]byte(enc)) 300 var val interface{} 301 302 // If Decode reads beyond what w.Write writes above, 303 // it will block, and the test will deadlock. 304 if err := NewDecoder(r).Decode(&val); err != nil { 305 t.Errorf("decoding %s: %v", enc, err) 306 } 307 r.Close() 308 w.Close() 309 } 310} 311 312func BenchmarkEncoderEncode(b *testing.B) { 313 b.ReportAllocs() 314 type T struct { 315 X, Y string 316 } 317 v := &T{"foo", "bar"} 318 b.RunParallel(func(pb *testing.PB) { 319 for pb.Next() { 320 if err := NewEncoder(io.Discard).Encode(v); err != nil { 321 b.Fatal(err) 322 } 323 } 324 }) 325} 326 327type tokenStreamCase struct { 328 json string 329 expTokens []interface{} 330} 331 332type decodeThis struct { 333 v interface{} 334} 335 336var tokenStreamCases = []tokenStreamCase{ 337 // streaming token cases 338 {json: `10`, expTokens: []interface{}{float64(10)}}, 339 {json: ` [10] `, expTokens: []interface{}{ 340 Delim('['), float64(10), Delim(']')}}, 341 {json: ` [false,10,"b"] `, expTokens: []interface{}{ 342 Delim('['), false, float64(10), "b", Delim(']')}}, 343 {json: `{ "a": 1 }`, expTokens: []interface{}{ 344 Delim('{'), "a", float64(1), Delim('}')}}, 345 {json: `{"a": 1, "b":"3"}`, expTokens: []interface{}{ 346 Delim('{'), "a", float64(1), "b", "3", Delim('}')}}, 347 {json: ` [{"a": 1},{"a": 2}] `, expTokens: []interface{}{ 348 Delim('['), 349 Delim('{'), "a", float64(1), Delim('}'), 350 Delim('{'), "a", float64(2), Delim('}'), 351 Delim(']')}}, 352 {json: `{"obj": {"a": 1}}`, expTokens: []interface{}{ 353 Delim('{'), "obj", Delim('{'), "a", float64(1), Delim('}'), 354 Delim('}')}}, 355 {json: `{"obj": [{"a": 1}]}`, expTokens: []interface{}{ 356 Delim('{'), "obj", Delim('['), 357 Delim('{'), "a", float64(1), Delim('}'), 358 Delim(']'), Delim('}')}}, 359 360 // streaming tokens with intermittent Decode() 361 {json: `{ "a": 1 }`, expTokens: []interface{}{ 362 Delim('{'), "a", 363 decodeThis{float64(1)}, 364 Delim('}')}}, 365 {json: ` [ { "a" : 1 } ] `, expTokens: []interface{}{ 366 Delim('['), 367 decodeThis{map[string]interface{}{"a": float64(1)}}, 368 Delim(']')}}, 369 {json: ` [{"a": 1},{"a": 2}] `, expTokens: []interface{}{ 370 Delim('['), 371 decodeThis{map[string]interface{}{"a": float64(1)}}, 372 decodeThis{map[string]interface{}{"a": float64(2)}}, 373 Delim(']')}}, 374 {json: `{ "obj" : [ { "a" : 1 } ] }`, expTokens: []interface{}{ 375 Delim('{'), "obj", Delim('['), 376 decodeThis{map[string]interface{}{"a": float64(1)}}, 377 Delim(']'), Delim('}')}}, 378 379 {json: `{"obj": {"a": 1}}`, expTokens: []interface{}{ 380 Delim('{'), "obj", 381 decodeThis{map[string]interface{}{"a": float64(1)}}, 382 Delim('}')}}, 383 {json: `{"obj": [{"a": 1}]}`, expTokens: []interface{}{ 384 Delim('{'), "obj", 385 decodeThis{[]interface{}{ 386 map[string]interface{}{"a": float64(1)}, 387 }}, 388 Delim('}')}}, 389 {json: ` [{"a": 1} {"a": 2}] `, expTokens: []interface{}{ 390 Delim('['), 391 decodeThis{map[string]interface{}{"a": float64(1)}}, 392 decodeThis{&SyntaxError{"expected comma after array element", 11}}, 393 }}, 394 {json: `{ "` + strings.Repeat("a", 513) + `" 1 }`, expTokens: []interface{}{ 395 Delim('{'), strings.Repeat("a", 513), 396 decodeThis{&SyntaxError{"expected colon after object key", 518}}, 397 }}, 398 {json: `{ "\a" }`, expTokens: []interface{}{ 399 Delim('{'), 400 &SyntaxError{"invalid character 'a' in string escape code", 3}, 401 }}, 402 {json: ` \a`, expTokens: []interface{}{ 403 &SyntaxError{"invalid character '\\\\' looking for beginning of value", 1}, 404 }}, 405} 406 407func TestDecodeInStream(t *testing.T) { 408 for ci, tcase := range tokenStreamCases { 409 410 dec := NewDecoder(strings.NewReader(tcase.json)) 411 for i, etk := range tcase.expTokens { 412 413 var tk interface{} 414 var err error 415 416 if dt, ok := etk.(decodeThis); ok { 417 etk = dt.v 418 err = dec.Decode(&tk) 419 } else { 420 tk, err = dec.Token() 421 } 422 if experr, ok := etk.(error); ok { 423 if err == nil || !reflect.DeepEqual(err, experr) { 424 t.Errorf("case %v: Expected error %#v in %q, but was %#v", ci, experr, tcase.json, err) 425 } 426 break 427 } else if err == io.EOF { 428 t.Errorf("case %v: Unexpected EOF in %q", ci, tcase.json) 429 break 430 } else if err != nil { 431 t.Errorf("case %v: Unexpected error '%#v' in %q", ci, err, tcase.json) 432 break 433 } 434 if !reflect.DeepEqual(tk, etk) { 435 t.Errorf(`case %v: %q @ %v expected %T(%v) was %T(%v)`, ci, tcase.json, i, etk, etk, tk, tk) 436 break 437 } 438 } 439 } 440} 441 442// Test from golang.org/issue/11893 443func TestHTTPDecoding(t *testing.T) { 444 const raw = `{ "foo": "bar" }` 445 446 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 447 w.Write([]byte(raw)) 448 })) 449 defer ts.Close() 450 res, err := http.Get(ts.URL) 451 if err != nil { 452 log.Fatalf("GET failed: %v", err) 453 } 454 defer res.Body.Close() 455 456 foo := struct { 457 Foo string 458 }{} 459 460 d := NewDecoder(res.Body) 461 err = d.Decode(&foo) 462 if err != nil { 463 t.Fatalf("Decode: %v", err) 464 } 465 if foo.Foo != "bar" { 466 t.Errorf("decoded %q; want \"bar\"", foo.Foo) 467 } 468 469 // make sure we get the EOF the second time 470 err = d.Decode(&foo) 471 if err != io.EOF { 472 t.Errorf("err = %v; want io.EOF", err) 473 } 474} 475