Skip to content

Commit cc4ddda

Browse files
authored
added unit tests for issues (#21)
* added unit tests for issues * resolve issues * add tests for unmarshal_from_json_map * rename argument for clarity
1 parent 489edf4 commit cc4ddda

File tree

4 files changed

+267
-34
lines changed

4 files changed

+267
-34
lines changed

unmarshal.go

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func Unmarshal(data []byte, v interface{}, options ...UnmarshalOption) (map[stri
3737
} else if !d.lexer.IsDelim('{') {
3838
return nil, ErrInvalidInput
3939
} else {
40-
d.populateStruct(v, result)
40+
d.populateStruct(false, v, result)
4141
}
4242
d.lexer.Consumed()
4343
if useMultipleErrors {
@@ -59,8 +59,8 @@ type decoder struct {
5959
lexer *jlexer.Lexer
6060
}
6161

62-
func (d *decoder) populateStruct(structInstance interface{}, result map[string]interface{}) (interface{}, bool) {
63-
doPopulate := !d.options.skipPopulateStruct || result == nil
62+
func (d *decoder) populateStruct(forcePopulate bool, structInstance interface{}, result map[string]interface{}) (interface{}, bool) {
63+
doPopulate := !d.options.skipPopulateStruct || forcePopulate
6464
var structValue reflect.Value
6565
if doPopulate {
6666
structValue = reflectStructValue(structInstance)
@@ -76,25 +76,26 @@ func (d *decoder) populateStruct(structInstance interface{}, result map[string]i
7676
d.lexer.WantColon()
7777
refInfo, exists := fields[key]
7878
if exists {
79-
value, isValidType := d.valueByReflectType(refInfo.t, false)
79+
value, isValidType := d.valueByReflectType(refInfo.t)
8080
if isValidType {
8181
if value != nil && doPopulate {
8282
field := refInfo.field(structValue)
8383
assignValue(field, value)
8484
}
85-
if result != nil {
86-
if !d.options.excludeKnownFieldsFromMap {
85+
if !d.options.excludeKnownFieldsFromMap {
86+
if result != nil {
8787
result[key] = value
8888
}
89-
} else if clone != nil {
90-
clone[key] = value
89+
if clone != nil {
90+
clone[key] = value
91+
}
9192
}
9293
} else {
9394
switch d.options.mode {
9495
case ModeFailOnFirstError:
9596
return nil, false
9697
case ModeFailOverToOriginalValue:
97-
if result != nil {
98+
if !forcePopulate {
9899
result[key] = value
99100
} else {
100101
clone[key] = value
@@ -109,14 +110,17 @@ func (d *decoder) populateStruct(structInstance interface{}, result map[string]i
109110
if result != nil {
110111
result[key] = value
111112
}
113+
if clone != nil {
114+
clone[key] = value
115+
}
112116
}
113117
d.lexer.WantComma()
114118
}
115119
d.lexer.Delim('}')
116120
return structInstance, true
117121
}
118122

119-
func (d *decoder) valueByReflectType(t reflect.Type, isPtr bool) (interface{}, bool) {
123+
func (d *decoder) valueByReflectType(t reflect.Type) (interface{}, bool) {
120124
if t.Implements(unmarshalerType) {
121125
result := reflect.New(t.Elem()).Interface()
122126
d.valueFromCustomUnmarshaler(result.(json.Unmarshaler))
@@ -160,7 +164,7 @@ func (d *decoder) valueByReflectType(t reflect.Type, isPtr bool) (interface{}, b
160164
if t.Elem().Kind() == reflect.Struct {
161165
return d.buildStruct(t.Elem())
162166
}
163-
value, valid := d.valueByReflectType(t.Elem(), true)
167+
value, valid := d.valueByReflectType(t.Elem())
164168
if value == nil {
165169
return nil, valid
166170
}
@@ -193,7 +197,7 @@ func (d *decoder) buildSlice(sliceType reflect.Type) (interface{}, bool) {
193197
sliceValue = reflect.MakeSlice(sliceType, 0, 0)
194198
}
195199
for !d.lexer.IsDelim(']') {
196-
current, valid := d.valueByReflectType(elemType, false)
200+
current, valid := d.valueByReflectType(elemType)
197201
if !valid {
198202
if d.options.mode != ModeFailOverToOriginalValue {
199203
d.drainLexerArray(nil)
@@ -223,7 +227,7 @@ func (d *decoder) buildArray(arrayType reflect.Type) (interface{}, bool) {
223227
arrayValue := reflect.New(arrayType).Elem()
224228
d.lexer.Delim('[')
225229
for i := 0; !d.lexer.IsDelim(']'); i++ {
226-
current, valid := d.valueByReflectType(elemType, false)
230+
current, valid := d.valueByReflectType(elemType)
227231
if !valid {
228232
if d.options.mode != ModeFailOverToOriginalValue {
229233
d.drainLexerArray(nil)
@@ -256,7 +260,7 @@ func (d *decoder) buildMap(mapType reflect.Type) (interface{}, bool) {
256260
valueType := mapType.Elem()
257261
mapValue := reflect.MakeMap(mapType)
258262
for !d.lexer.IsDelim('}') {
259-
key, valid := d.valueByReflectType(keyType, false)
263+
key, valid := d.valueByReflectType(keyType)
260264
if !valid {
261265
if d.options.mode != ModeFailOverToOriginalValue {
262266
d.lexer.WantColon()
@@ -275,7 +279,7 @@ func (d *decoder) buildMap(mapType reflect.Type) (interface{}, bool) {
275279
return result, true
276280
}
277281
d.lexer.WantColon()
278-
value, valid := d.valueByReflectType(valueType, false)
282+
value, valid := d.valueByReflectType(valueType)
279283
if !valid {
280284
if d.options.mode != ModeFailOverToOriginalValue {
281285
d.lexer.WantComma()
@@ -308,10 +312,10 @@ func (d *decoder) buildStruct(structType reflect.Type) (interface{}, bool) {
308312
value := reflect.New(structType).Interface()
309313
handler, ok := value.(JSONDataHandler)
310314
if !ok {
311-
return d.populateStruct(value, nil)
315+
return d.populateStruct(true, value, nil)
312316
}
313317
data := make(map[string]interface{})
314-
result, valid := d.populateStruct(value, data)
318+
result, valid := d.populateStruct(true, value, data)
315319
if valid {
316320
handler.HandleJSONData(data)
317321
}

unmarshal_from_json_map.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func UnmarshalFromJSONMap(data map[string]interface{}, v interface{}, options ..
4242
d := &mapDecoder{options: opts}
4343
result := make(map[string]interface{})
4444
if data != nil {
45-
d.populateStruct(nil, data, v, result)
45+
d.populateStruct(false, nil, data, v, result)
4646
}
4747
if opts.mode == ModeAllowMultipleErrors || opts.mode == ModeFailOverToOriginalValue {
4848
if len(d.errs) == 0 {
@@ -64,8 +64,8 @@ type mapDecoder struct {
6464
errs []error
6565
}
6666

67-
func (m *mapDecoder) populateStruct(path []string, data map[string]interface{}, structInstance interface{}, result map[string]interface{}) (interface{}, bool) {
68-
doPopulate := !m.options.skipPopulateStruct || result == nil
67+
func (m *mapDecoder) populateStruct(forcePopulate bool, path []string, data map[string]interface{}, structInstance interface{}, result map[string]interface{}) (interface{}, bool) {
68+
doPopulate := !m.options.skipPopulateStruct || forcePopulate
6969
var structValue reflect.Value
7070
if doPopulate {
7171
structValue = reflectStructValue(structInstance)
@@ -74,14 +74,14 @@ func (m *mapDecoder) populateStruct(path []string, data map[string]interface{},
7474
for key, inputValue := range data {
7575
refInfo, exists := fields[key]
7676
if exists {
77-
value, isValidType := m.valueByReflectType(append(path, key), inputValue, refInfo.t, false)
77+
value, isValidType := m.valueByReflectType(append(path, key), inputValue, refInfo.t)
7878
if isValidType {
7979
if value != nil && doPopulate {
8080
field := refInfo.field(structValue)
8181
assignValue(field, value)
8282
}
83-
if result != nil {
84-
if !m.options.excludeKnownFieldsFromMap {
83+
if !m.options.excludeKnownFieldsFromMap {
84+
if result != nil {
8585
result[key] = value
8686
}
8787
}
@@ -90,7 +90,7 @@ func (m *mapDecoder) populateStruct(path []string, data map[string]interface{},
9090
case ModeFailOnFirstError:
9191
return nil, false
9292
case ModeFailOverToOriginalValue:
93-
if result != nil {
93+
if !forcePopulate {
9494
result[key] = value
9595
} else {
9696
return data, false
@@ -106,7 +106,7 @@ func (m *mapDecoder) populateStruct(path []string, data map[string]interface{},
106106
return structInstance, true
107107
}
108108

109-
func (m *mapDecoder) valueByReflectType(path []string, v interface{}, t reflect.Type, isPtr bool) (interface{}, bool) {
109+
func (m *mapDecoder) valueByReflectType(path []string, v interface{}, t reflect.Type) (interface{}, bool) {
110110
if t.Implements(unmarshalerFromJSONMapType) {
111111
result := reflect.New(t.Elem()).Interface()
112112
m.valueFromCustomUnmarshaler(v, result.(UnmarshalerFromJSONMap))
@@ -149,7 +149,7 @@ func (m *mapDecoder) valueByReflectType(path []string, v interface{}, t reflect.
149149
if t.Elem().Kind() == reflect.Struct {
150150
return m.buildStruct(path, v, t.Elem())
151151
}
152-
value, valid := m.valueByReflectType(path, v, t.Elem(), true)
152+
value, valid := m.valueByReflectType(path, v, t.Elem())
153153
if value == nil {
154154
return nil, valid
155155
}
@@ -181,7 +181,7 @@ func (m *mapDecoder) buildSlice(path []string, v interface{}, sliceType reflect.
181181
sliceValue = reflect.MakeSlice(sliceType, 0, 0)
182182
}
183183
for _, element := range arr {
184-
current, valid := m.valueByReflectType(path, element, elemType, false)
184+
current, valid := m.valueByReflectType(path, element, elemType)
185185
if !valid {
186186
if m.options.mode != ModeFailOverToOriginalValue {
187187
return nil, true
@@ -205,7 +205,7 @@ func (m *mapDecoder) buildArray(path []string, v interface{}, arrayType reflect.
205205
elemType := arrayType.Elem()
206206
arrayValue := reflect.New(arrayType).Elem()
207207
for i, element := range arr {
208-
current, valid := m.valueByReflectType(path, element, elemType, false)
208+
current, valid := m.valueByReflectType(path, element, elemType)
209209
if !valid {
210210
if m.options.mode != ModeFailOverToOriginalValue {
211211
return nil, true
@@ -233,14 +233,14 @@ func (m *mapDecoder) buildMap(path []string, v interface{}, mapType reflect.Type
233233
mapValue := reflect.MakeMap(mapType)
234234
for inputKey, inputValue := range mp {
235235
keyPath := append(path, inputKey)
236-
key, valid := m.valueByReflectType(keyPath, inputKey, keyType, false)
236+
key, valid := m.valueByReflectType(keyPath, inputKey, keyType)
237237
if !valid {
238238
if m.options.mode != ModeFailOverToOriginalValue {
239239
return nil, true
240240
}
241241
return v, true
242242
}
243-
value, valid := m.valueByReflectType(keyPath, inputValue, valueType, false)
243+
value, valid := m.valueByReflectType(keyPath, inputValue, valueType)
244244
if !valid {
245245
if m.options.mode != ModeFailOverToOriginalValue {
246246
return nil, true
@@ -264,10 +264,10 @@ func (m *mapDecoder) buildStruct(path []string, v interface{}, structType reflec
264264
value := reflect.New(structType).Interface()
265265
handler, ok := value.(JSONDataHandler)
266266
if !ok {
267-
return m.populateStruct(path, mp, value, nil)
267+
return m.populateStruct(true, path, mp, value, nil)
268268
}
269269
data := make(map[string]interface{})
270-
result, valid := m.populateStruct(path, mp, value, data)
270+
result, valid := m.populateStruct(true, path, mp, value, data)
271271
if valid {
272272
handler.HandleJSONData(data)
273273
}

unmarshal_from_json_map_test.go

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2305,7 +2305,14 @@ func TestUnmarshalFromJSONMapJSONDataHandler(t *testing.T) {
23052305
func TestUnmarshalFromJSONMapExcludeKnownFieldsFromMap(t *testing.T) {
23062306
t.Run("test_exclude_known_fields_from_map_with_empty_map", func(t *testing.T) {
23072307
p := Person{}
2308-
result, err := UnmarshalFromJSONMap(map[string]interface{}{"firstName": "string_firstName", "lastName": "string_LastName"}, &p, WithExcludeKnownFieldsFromMap(true))
2308+
result, err := UnmarshalFromJSONMap(
2309+
map[string]interface{}{
2310+
"firstName": "string_firstName",
2311+
"lastName": "string_LastName",
2312+
},
2313+
&p,
2314+
WithExcludeKnownFieldsFromMap(true),
2315+
)
23092316
if err != nil {
23102317
t.Errorf("unexpected error %v", err)
23112318
}
@@ -2316,7 +2323,15 @@ func TestUnmarshalFromJSONMapExcludeKnownFieldsFromMap(t *testing.T) {
23162323

23172324
t.Run("test_exclude_known_fields_from_map", func(t *testing.T) {
23182325
p := Person{}
2319-
result, err := UnmarshalFromJSONMap(map[string]interface{}{"firstName": "string_firstName", "lastName": "string_LastName", "unknown": "string_unknown"}, &p, WithExcludeKnownFieldsFromMap(true))
2326+
result, err := UnmarshalFromJSONMap(
2327+
map[string]interface{}{
2328+
"firstName": "string_firstName",
2329+
"lastName": "string_LastName",
2330+
"unknown": "string_unknown",
2331+
},
2332+
&p,
2333+
WithExcludeKnownFieldsFromMap(true),
2334+
)
23202335
if err != nil {
23212336
t.Errorf("unexpected error %v", err)
23222337
}
@@ -2330,3 +2345,101 @@ func TestUnmarshalFromJSONMapExcludeKnownFieldsFromMap(t *testing.T) {
23302345
}
23312346
})
23322347
}
2348+
2349+
func TestUnmarshalFromJSONMapNestedSkipPopulate(t *testing.T) {
2350+
t.Run("TestUnmarshalFromJSONMapNestedSkipPopulate", func(t *testing.T) {
2351+
p := &nestedSkipPopulateParent{}
2352+
result, err := UnmarshalFromJSONMap(
2353+
map[string]interface{}{"child": map[string]interface{}{"foo": "value"}},
2354+
p,
2355+
WithSkipPopulateStruct(true),
2356+
)
2357+
if err != nil {
2358+
t.Errorf("unexpected error %v", err)
2359+
}
2360+
value, exists := result["child"]
2361+
if !exists {
2362+
t.Error("missing child element in result map")
2363+
}
2364+
child, ok := value.(nestedSkipPopulateChild)
2365+
if !ok {
2366+
t.Errorf("invalid child type %T in result map", child)
2367+
}
2368+
if child.Foo != "value" {
2369+
t.Errorf("invalid value '%s' in child", child.Foo)
2370+
}
2371+
})
2372+
t.Run("TestUnmarshalFromJSONMapNestedSkipPopulate_with_ModeFailOverToOriginalValue", func(t *testing.T) {
2373+
p := &nestedSkipPopulateParent{}
2374+
result, err := UnmarshalFromJSONMap(
2375+
map[string]interface{}{"child": map[string]interface{}{"foo": float64(12)}},
2376+
p,
2377+
WithMode(ModeFailOverToOriginalValue),
2378+
WithSkipPopulateStruct(true),
2379+
)
2380+
if err == nil {
2381+
t.Error("expected error")
2382+
}
2383+
value, exists := result["child"]
2384+
if !exists {
2385+
t.Error("missing child element in result map")
2386+
}
2387+
child, ok := value.(map[string]interface{})
2388+
if !ok {
2389+
t.Errorf("invalid child type %T in result map", child)
2390+
}
2391+
if child["foo"] != float64(12) {
2392+
t.Errorf("invalid value '%v' in child", child["foo"])
2393+
}
2394+
})
2395+
t.Run("TestUnmarshalFromJSONMapNestedSkipPopulate_all_fields_exist_in_root_struct", func(t *testing.T) {
2396+
s := &failOverStruct{}
2397+
result, err := UnmarshalFromJSONMap(
2398+
map[string]interface{}{"a": "a_val", "b": float64(12), "c": "c_val"},
2399+
s,
2400+
WithMode(ModeFailOverToOriginalValue),
2401+
WithSkipPopulateStruct(true),
2402+
)
2403+
if err == nil {
2404+
t.Error("expected error")
2405+
}
2406+
if result["a"] != "a_val" {
2407+
t.Errorf("invalid value '%v' in a", result["a"])
2408+
}
2409+
if result["b"] != float64(12) {
2410+
t.Errorf("invalid value '%v' in a", result["b"])
2411+
}
2412+
if result["c"] != "c_val" {
2413+
t.Errorf("invalid value '%v' in a", result["c"])
2414+
}
2415+
})
2416+
t.Run("TestUnmarshalFromJSONMapNestedSkipPopulate_all_fields_exist_in_nested_struct", func(t *testing.T) {
2417+
s := &failOverParent{}
2418+
result, err := UnmarshalFromJSONMap(
2419+
map[string]interface{}{"child": map[string]interface{}{"a": "a_val", "b": float64(12), "c": "c_val"}},
2420+
s,
2421+
WithMode(ModeFailOverToOriginalValue),
2422+
WithSkipPopulateStruct(true),
2423+
)
2424+
if err == nil {
2425+
t.Error("expected error")
2426+
}
2427+
val, ok := result["child"]
2428+
if !ok {
2429+
t.Error("missing child in result value")
2430+
}
2431+
child, ok := val.(map[string]interface{})
2432+
if !ok {
2433+
t.Error("invalid child type in result value")
2434+
}
2435+
if child["a"] != "a_val" {
2436+
t.Errorf("invalid value '%v' in a", child["a"])
2437+
}
2438+
if child["b"] != float64(12) {
2439+
t.Errorf("invalid value '%v' in a", child["b"])
2440+
}
2441+
if child["c"] != "c_val" {
2442+
t.Errorf("invalid value '%v' in a", child["c"])
2443+
}
2444+
})
2445+
}

0 commit comments

Comments
 (0)