You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2026/06/01 19:35:26 UTC

(juneau) branch master updated: Add regression guard for empty-array deserialization into abstract collections

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 <ja...@apache.org>
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