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