This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 9363babe27 Add regression guard for empty-array deserialization into 
abstract collections
9363babe27 is described below

commit 9363babe275fa8b7a52af19c36cf313168864572
Author: James Bognar <[email protected]>
AuthorDate: Mon Jun 1 15:35:20 2026 -0400

    Add regression guard for empty-array deserialization into abstract 
collections
    
    Test-only change adding regression cases to JsonParser_Test covering
    deserialization of an empty JSON array ([]) and empty JSON object ({}) into 
an
    abstract, currently-null bean property of type Set, List, or Map — including
    enum element types (Set<Enum>, List<Enum>, Map<String,Enum>) as well as the
    non-enum (String) variants, plus a populated-path guard.
    
    This pins the behavior fixed when the abstract-collection materialization 
helper
    (BeanPropertyMeta.createDefaultCollectionForAbstractType) was hardened into 
a
    total best-effort-then-fallback resolver, so a future change cannot silently
    regress empty-array/empty-object materialization (empty [] into an abstract 
null
    Set<Enum> previously threw BeanRuntimeException: "property type is 
abstract, and
    the property value is currently null"). No production code change.
---
 .../org/apache/juneau/json/JsonParser_Test.java    | 100 +++++++++++++++++++++
 juneau-utest/test-run-history.tsv                  |   1 +
 2 files changed, 101 insertions(+)

diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/json/JsonParser_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/json/JsonParser_Test.java
index fae8612667..a260159377 100755
--- a/juneau-utest/src/test/java/org/apache/juneau/json/JsonParser_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/json/JsonParser_Test.java
@@ -22,6 +22,7 @@ import static org.apache.juneau.junit.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
 
 import java.io.*;
+import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.collections.*;
@@ -170,4 +171,103 @@ class JsonParser_Test extends TestBase {
        private static Reader reader(String in) {
                return new CloseableStringReader(in);
        }
+
+       
//====================================================================================================
+       // TODO-147: Empty JSON array/object into an abstract, currently-null 
collection property.
+       // Regression discovered downstream (IRS) on the 8.2.0 -> 9.1.0 
upgrade: an empty array ([]) into an
+       // abstract, null Set<Enum> field threw BeanRuntimeException ("property 
type is abstract, and the
+       // property value is currently null").  FINISHED-127 hardened the 
abstract-collection materialization
+       // helper.  These tests pin the full empty-collection matrix as a 
permanent regression guard.
+       
//====================================================================================================
+
+       public enum B_Enum { A, B, C }
+
+       public static class B01_Bean {
+               private Set<B_Enum> v;
+               public Set<B_Enum> getV() { return v; }
+               public B01_Bean setV(Set<B_Enum> x) { v = x; return this; }
+       }
+
+       // Headline case, mirroring the exact repro from TODO-147 (abstract 
Set<Enum>, empty array, null field).
+       @Test void b01_emptyArrayIntoAbstractSetOfEnum() throws Exception {
+               var p2 = 
JsonParser.create().ignoreUnknownBeanProperties().build();
+               var x = p2.parse("{\"v\":[]}", B01_Bean.class);
+               assertNotNull(x.getV());
+               assertTrue(x.getV().isEmpty());
+               assertInstanceOf(LinkedHashSet.class, x.getV());
+       }
+
+       // Non-empty array of the same shape must still populate (guards the 
populated path).
+       @Test void b02_populatedArrayIntoAbstractSetOfEnum() throws Exception {
+               var x = p.parse("{\"v\":[\"A\",\"B\"]}", B01_Bean.class);
+               assertInstanceOf(LinkedHashSet.class, x.getV());
+               assertEquals(Set.of(B_Enum.A, B_Enum.B), x.getV());
+       }
+
+       public static class B03_Bean {
+               private List<B_Enum> v;
+               public List<B_Enum> getV() { return v; }
+               public B03_Bean setV(List<B_Enum> x) { v = x; return this; }
+       }
+
+       // For a List field the parser's native JsonList is itself a List, so 
it is assigned directly (no
+       // abstract-materialization coercion is needed).  Either way the 
contract is: non-null, empty List.
+       @Test void b03_emptyArrayIntoAbstractListOfEnum() throws Exception {
+               var x = p.parse("{\"v\":[]}", B03_Bean.class);
+               assertNotNull(x.getV());
+               assertTrue(x.getV().isEmpty());
+               assertInstanceOf(List.class, x.getV());
+       }
+
+       public static class B04_Bean {
+               private Map<String,B_Enum> v;
+               public Map<String,B_Enum> getV() { return v; }
+               public B04_Bean setV(Map<String,B_Enum> x) { v = x; return 
this; }
+       }
+
+       @Test void b04_emptyObjectIntoAbstractMapOfEnum() throws Exception {
+               var x = p.parse("{\"v\":{}}", B04_Bean.class);
+               assertNotNull(x.getV());
+               assertTrue(x.getV().isEmpty());
+               assertInstanceOf(LinkedHashMap.class, x.getV());
+       }
+
+       public static class B05_Bean {
+               private Set<String> v;
+               public Set<String> getV() { return v; }
+               public B05_Bean setV(Set<String> x) { v = x; return this; }
+       }
+
+       @Test void b05_emptyArrayIntoAbstractSetOfString() throws Exception {
+               var x = p.parse("{\"v\":[]}", B05_Bean.class);
+               assertNotNull(x.getV());
+               assertTrue(x.getV().isEmpty());
+               assertInstanceOf(LinkedHashSet.class, x.getV());
+       }
+
+       public static class B06_Bean {
+               private List<String> v;
+               public List<String> getV() { return v; }
+               public B06_Bean setV(List<String> x) { v = x; return this; }
+       }
+
+       @Test void b06_emptyArrayIntoAbstractListOfString() throws Exception {
+               var x = p.parse("{\"v\":[]}", B06_Bean.class);
+               assertNotNull(x.getV());
+               assertTrue(x.getV().isEmpty());
+               assertInstanceOf(List.class, x.getV());
+       }
+
+       public static class B07_Bean {
+               private Map<String,String> v;
+               public Map<String,String> getV() { return v; }
+               public B07_Bean setV(Map<String,String> x) { v = x; return 
this; }
+       }
+
+       @Test void b07_emptyObjectIntoAbstractMapOfString() throws Exception {
+               var x = p.parse("{\"v\":{}}", B07_Bean.class);
+               assertNotNull(x.getV());
+               assertTrue(x.getV().isEmpty());
+               assertInstanceOf(LinkedHashMap.class, x.getV());
+       }
 }
\ No newline at end of file
diff --git a/juneau-utest/test-run-history.tsv 
b/juneau-utest/test-run-history.tsv
index 716f92e14d..a4504cd376 100644
--- a/juneau-utest/test-run-history.tsv
+++ b/juneau-utest/test-run-history.tsv
@@ -61,3 +61,4 @@ timestamp     git_sha branch  tests_run       failures        
errors  skipped surefire_sec    wall_sec
 2026-06-01T15:50:15Z   f167a9dccbc7    master  126165  0       0       26      
180
 2026-06-01T17:10:12Z   6f9df91cec12    master  126175  0       0       26      
181
 2026-06-01T18:55:33Z   8eceb21bb5b9    master  126190  0       0       26      
185
+2026-06-01T19:34:19Z   f8486683b13c    master  126197  0       0       26      
185

Reply via email to