1package misc_tests
2
3import (
4	"encoding/json"
5	"github.com/json-iterator/go"
6	"reflect"
7	"strings"
8	"testing"
9)
10
11type Level1 struct {
12	Hello []Level2
13}
14
15type Level2 struct {
16	World string
17}
18
19func Test_deep_nested(t *testing.T) {
20	type unstructured interface{}
21
22	testcases := []struct {
23		name        string
24		data        []byte
25		expectError string
26	}{
27		{
28			name:        "array under maxDepth",
29			data:        []byte(`{"a":` + strings.Repeat(`[`, 10000-1) + strings.Repeat(`]`, 10000-1) + `}`),
30			expectError: "",
31		},
32		{
33			name:        "array over maxDepth",
34			data:        []byte(`{"a":` + strings.Repeat(`[`, 10000) + strings.Repeat(`]`, 10000) + `}`),
35			expectError: "max depth",
36		},
37		{
38			name:        "object under maxDepth",
39			data:        []byte(`{"a":` + strings.Repeat(`{"a":`, 10000-1) + `0` + strings.Repeat(`}`, 10000-1) + `}`),
40			expectError: "",
41		},
42		{
43			name:        "object over maxDepth",
44			data:        []byte(`{"a":` + strings.Repeat(`{"a":`, 10000) + `0` + strings.Repeat(`}`, 10000) + `}`),
45			expectError: "max depth",
46		},
47	}
48
49	targets := []struct {
50		name string
51		new  func() interface{}
52	}{
53		{
54			name: "unstructured",
55			new: func() interface{} {
56				var v interface{}
57				return &v
58			},
59		},
60		{
61			name: "typed named field",
62			new: func() interface{} {
63				v := struct {
64					A interface{} `json:"a"`
65				}{}
66				return &v
67			},
68		},
69		{
70			name: "typed missing field",
71			new: func() interface{} {
72				v := struct {
73					B interface{} `json:"b"`
74				}{}
75				return &v
76			},
77		},
78		{
79			name: "typed 1 field",
80			new: func() interface{} {
81				v := struct {
82					A interface{} `json:"a"`
83				}{}
84				return &v
85			},
86		},
87		{
88			name: "typed 2 field",
89			new: func() interface{} {
90				v := struct {
91					A interface{} `json:"a"`
92					B interface{} `json:"b"`
93				}{}
94				return &v
95			},
96		},
97		{
98			name: "typed 3 field",
99			new: func() interface{} {
100				v := struct {
101					A interface{} `json:"a"`
102					B interface{} `json:"b"`
103					C interface{} `json:"c"`
104				}{}
105				return &v
106			},
107		},
108		{
109			name: "typed 4 field",
110			new: func() interface{} {
111				v := struct {
112					A interface{} `json:"a"`
113					B interface{} `json:"b"`
114					C interface{} `json:"c"`
115					D interface{} `json:"d"`
116				}{}
117				return &v
118			},
119		},
120		{
121			name: "typed 5 field",
122			new: func() interface{} {
123				v := struct {
124					A interface{} `json:"a"`
125					B interface{} `json:"b"`
126					C interface{} `json:"c"`
127					D interface{} `json:"d"`
128					E interface{} `json:"e"`
129				}{}
130				return &v
131			},
132		},
133		{
134			name: "typed 6 field",
135			new: func() interface{} {
136				v := struct {
137					A interface{} `json:"a"`
138					B interface{} `json:"b"`
139					C interface{} `json:"c"`
140					D interface{} `json:"d"`
141					E interface{} `json:"e"`
142					F interface{} `json:"f"`
143				}{}
144				return &v
145			},
146		},
147		{
148			name: "typed 7 field",
149			new: func() interface{} {
150				v := struct {
151					A interface{} `json:"a"`
152					B interface{} `json:"b"`
153					C interface{} `json:"c"`
154					D interface{} `json:"d"`
155					E interface{} `json:"e"`
156					F interface{} `json:"f"`
157					G interface{} `json:"g"`
158				}{}
159				return &v
160			},
161		},
162		{
163			name: "typed 8 field",
164			new: func() interface{} {
165				v := struct {
166					A interface{} `json:"a"`
167					B interface{} `json:"b"`
168					C interface{} `json:"c"`
169					D interface{} `json:"d"`
170					E interface{} `json:"e"`
171					F interface{} `json:"f"`
172					G interface{} `json:"g"`
173					H interface{} `json:"h"`
174				}{}
175				return &v
176			},
177		},
178		{
179			name: "typed 9 field",
180			new: func() interface{} {
181				v := struct {
182					A interface{} `json:"a"`
183					B interface{} `json:"b"`
184					C interface{} `json:"c"`
185					D interface{} `json:"d"`
186					E interface{} `json:"e"`
187					F interface{} `json:"f"`
188					G interface{} `json:"g"`
189					H interface{} `json:"h"`
190					I interface{} `json:"i"`
191				}{}
192				return &v
193			},
194		},
195		{
196			name: "typed 10 field",
197			new: func() interface{} {
198				v := struct {
199					A interface{} `json:"a"`
200					B interface{} `json:"b"`
201					C interface{} `json:"c"`
202					D interface{} `json:"d"`
203					E interface{} `json:"e"`
204					F interface{} `json:"f"`
205					G interface{} `json:"g"`
206					H interface{} `json:"h"`
207					I interface{} `json:"i"`
208					J interface{} `json:"j"`
209				}{}
210				return &v
211			},
212		},
213		{
214			name: "typed 11 field",
215			new: func() interface{} {
216				v := struct {
217					A interface{} `json:"a"`
218					B interface{} `json:"b"`
219					C interface{} `json:"c"`
220					D interface{} `json:"d"`
221					E interface{} `json:"e"`
222					F interface{} `json:"f"`
223					G interface{} `json:"g"`
224					H interface{} `json:"h"`
225					I interface{} `json:"i"`
226					J interface{} `json:"j"`
227					K interface{} `json:"k"`
228				}{}
229				return &v
230			},
231		},
232	}
233
234	for _, tc := range testcases {
235		t.Run(tc.name, func(t *testing.T) {
236			for _, target := range targets {
237				t.Run(target.name, func(t *testing.T) {
238					err := jsoniter.Unmarshal(tc.data, target.new())
239					if len(tc.expectError) == 0 {
240						if err != nil {
241							t.Errorf("unexpected error: %v", err)
242						}
243					} else {
244						if err == nil {
245							t.Errorf("expected error, got none")
246						} else if !strings.Contains(err.Error(), tc.expectError) {
247							t.Errorf("expected error containing '%s', got: %v", tc.expectError, err)
248						}
249					}
250				})
251			}
252		})
253	}
254}
255
256func Test_nested(t *testing.T) {
257	iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
258	l1 := Level1{}
259	for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
260		switch l1Field {
261		case "hello":
262			l2Array := []Level2{}
263			for iter.ReadArray() {
264				l2 := Level2{}
265				for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {
266					switch l2Field {
267					case "world":
268						l2.World = iter.ReadString()
269					default:
270						iter.ReportError("bind l2", "unexpected field: "+l2Field)
271					}
272				}
273				l2Array = append(l2Array, l2)
274			}
275			l1.Hello = l2Array
276		default:
277			iter.ReportError("bind l1", "unexpected field: "+l1Field)
278		}
279	}
280	if !reflect.DeepEqual(l1, Level1{
281		Hello: []Level2{
282			{World: "value1"},
283			{World: "value2"},
284		},
285	}) {
286		t.Fatal(l1)
287	}
288}
289
290func Benchmark_jsoniter_nested(b *testing.B) {
291	for n := 0; n < b.N; n++ {
292		iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
293		l1 := Level1{}
294		for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
295			switch l1Field {
296			case "hello":
297				l1.Hello = readLevel1Hello(iter)
298			default:
299				iter.Skip()
300			}
301		}
302	}
303}
304
305func readLevel1Hello(iter *jsoniter.Iterator) []Level2 {
306	l2Array := make([]Level2, 0, 2)
307	for iter.ReadArray() {
308		l2 := Level2{}
309		for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {
310			switch l2Field {
311			case "world":
312				l2.World = iter.ReadString()
313			default:
314				iter.Skip()
315			}
316		}
317		l2Array = append(l2Array, l2)
318	}
319	return l2Array
320}
321
322func Benchmark_json_nested(b *testing.B) {
323	for n := 0; n < b.N; n++ {
324		l1 := Level1{}
325		json.Unmarshal([]byte(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`), &l1)
326	}
327}
328