|
| 1 | +import x.json2 |
| 2 | + |
| 3 | +// Test for issue #26503: Decoder incorrectly reads nested object fields |
| 4 | +// When a required field appears after unmatched fields with nested objects/arrays, |
| 5 | +// the decoder was incorrectly picking up values from nested structures. |
| 6 | + |
| 7 | +struct Foo { |
| 8 | + id string @[required] |
| 9 | + title string @[required] |
| 10 | +} |
| 11 | + |
| 12 | +fn test_decode_with_nested_objects_field_order() { |
| 13 | + // Test case 1: required fields appear before the nested array |
| 14 | + s1 := '{"id":"sss","title":"ttt","thumb":[{ "url":"i1.jpg","id":"000"}]}' |
| 15 | + f1 := json2.decode[Foo](s1)! |
| 16 | + |
| 17 | + assert f1.id == 'sss', 'f1.id should be "sss" but got "${f1.id}"' |
| 18 | + assert f1.title == 'ttt', 'f1.title should be "ttt" but got "${f1.title}"' |
| 19 | + |
| 20 | + // Test case 2: required fields appear after the nested array |
| 21 | + s2 := '{"title":"ttt","thumb":[{ "url":"i1.jpg","id":"000"}],"id":"sss"}' |
| 22 | + f2 := json2.decode[Foo](s2)! |
| 23 | + |
| 24 | + assert f2.id == 'sss', 'f2.id should be "sss" but got "${f2.id}"' |
| 25 | + assert f2.title == 'ttt', 'f2.title should be "ttt" but got "${f2.title}"' |
| 26 | + |
| 27 | + // Test case 3: nested array appears between required fields |
| 28 | + s3 := '{"id":"sss","thumb":[{ "url":"i1.jpg","id":"000"}],"title":"ttt"}' |
| 29 | + f3 := json2.decode[Foo](s3)! |
| 30 | + |
| 31 | + assert f3.id == 'sss', 'f3.id should be "sss" but got "${f3.id}"' |
| 32 | + assert f3.title == 'ttt', 'f3.title should be "ttt" but got "${f3.title}"' |
| 33 | +} |
| 34 | + |
| 35 | +fn test_decode_with_deeply_nested_objects() { |
| 36 | + // Test with deeply nested structures to ensure complete skipping |
| 37 | + s := '{"id":"outer","data":{"nested":{"deep":{"id":"inner","title":"inner_title"}}},"title":"outer_title"}' |
| 38 | + f := json2.decode[Foo](s)! |
| 39 | + |
| 40 | + assert f.id == 'outer', 'id should be "outer" but got "${f.id}"' |
| 41 | + assert f.title == 'outer_title', 'title should be "outer_title" but got "${f.title}"' |
| 42 | +} |
| 43 | + |
| 44 | +fn test_decode_with_multiple_nested_arrays() { |
| 45 | + // Test with multiple nested arrays to ensure complete skipping |
| 46 | + s := '{"id":"correct","items":[{"id":"a"},{"id":"b"}],"more":[{"id":"c"}],"title":"correct_title"}' |
| 47 | + f := json2.decode[Foo](s)! |
| 48 | + |
| 49 | + assert f.id == 'correct', 'id should be "correct" but got "${f.id}"' |
| 50 | + assert f.title == 'correct_title', 'title should be "correct_title" but got "${f.title}"' |
| 51 | +} |
| 52 | + |
| 53 | +struct BarWithOptional { |
| 54 | + id string @[required] |
| 55 | + title ?string |
| 56 | + extra string |
| 57 | +} |
| 58 | + |
| 59 | +fn test_decode_optional_fields_with_nested() { |
| 60 | + // Test optional fields work correctly with nested structures |
| 61 | + s := '{"id":"main","nested":{"title":"nested_title"},"extra":"extra_value"}' |
| 62 | + b := json2.decode[BarWithOptional](s)! |
| 63 | + |
| 64 | + assert b.id == 'main', 'id should be "main" but got "${b.id}"' |
| 65 | + assert b.extra == 'extra_value', 'extra should be "extra_value" but got "${b.extra}"' |
| 66 | + if title := b.title { |
| 67 | + assert false, 'title should be none but got "${title}"' |
| 68 | + } |
| 69 | +} |
0 commit comments