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