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  }