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 445f05fd70 Parquet fix
445f05fd70 is described below
commit 445f05fd701cbfcd5184138c516136cd6fe1fba4
Author: James Bognar <[email protected]>
AuthorDate: Thu Mar 19 08:25:39 2026 -0400
Parquet fix
---
.../main/java/org/apache/juneau/BeanSession.java | 4 ++
.../juneau/parquet/ParquetParserSession.java | 10 +++--
.../juneau/a/rttests/RoundTripTest_Base.java | 48 +++++++++-------------
3 files changed, 30 insertions(+), 32 deletions(-)
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
index 9b25d88c15..9535ba2942 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
@@ -1200,6 +1200,10 @@ public class BeanSession extends ContextSession {
var fc = swap.getSwapClass();
if (nc.isAssignableFrom(tc) &&
fc.isAssignableFrom(value.getClass()))
return (T)swap.unswap(this, value, to);
+ if (nc.isAssignableFrom(tc) &&
Map.class.isAssignableFrom(fc.inner()) && value instanceof Map) {
+ value = convertToMemberType(null,
value, fc.inner());
+ return (T)swap.unswap(this, value, to);
+ }
var fcm = getClassMeta(fc);
if (fcm.isNumber() && value instanceof Number
value2) {
value = convertToMemberType(null,
value2, fc.inner());
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parquet/ParquetParserSession.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parquet/ParquetParserSession.java
index 995a648d58..398cf5cfeb 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parquet/ParquetParserSession.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parquet/ParquetParserSession.java
@@ -169,10 +169,12 @@ public class ParquetParserSession extends
InputStreamParserSession {
return (T)convertToType(list, type);
}
if (type.isCollection()) {
- var coll = type.newInstance();
+ Object coll = type.newInstance();
+ if (coll == null)
+ coll = new ArrayList<>();
if (inner != null)
((Collection<Object>)coll).add(convertToType(prepareMapForBean(inner,
type.getElementType()), type.getElementType()));
- return coll;
+ return (T)coll;
}
return (T)convertToType(prepareMapForBean(inner, type),
type);
}
@@ -914,7 +916,7 @@ public class ParquetParserSession extends
InputStreamParserSession {
var listBeanColumns = groupListBeanColumns(columnData.keySet(),
elementType);
var mapColumns = groupMapColumns(columnData.keySet());
for (int i = 0; i < numRows; i++) {
- var row = new LinkedHashMap<String,Object>();
+ var row = new JsonMap();
for (var e : columnData.entrySet()) {
var fullPath = e.getKey();
var rowRelPath = rowRelativePath(fullPath);
@@ -1033,7 +1035,7 @@ public class ParquetParserSession extends
InputStreamParserSession {
int listSize = firstList.size();
var result = new ArrayList<>(listSize);
for (int j = 0; j < listSize; j++) {
- var elemMap = new LinkedHashMap<String,Object>();
+ var elemMap = new JsonMap();
for (var col : columnsInGroup) {
var colValues = columnData.get(col.fullPath());
var rowList = (colValues != null && rowIndex <
colValues.size()) ? colValues.get(rowIndex) : null;
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripTest_Base.java
b/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripTest_Base.java
index 58657a395f..913df9d7fc 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripTest_Base.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripTest_Base.java
@@ -168,9 +168,7 @@ public abstract class RoundTripTest_Base extends TestBase {
tester(28, "Parquet - default")
.serializer(ParquetSerializer.create().addBeanTypes())
.parser(ParquetParser.create())
- // TODO: Revisit skip conditions as Parquet support
improves:
- // - JsonList/JsonMap: static schema vs mixed types
- // TODO: Revisit skip conditions as Parquet support
improves:
+ // Parquet skip conditions for inherent format
limitations:
// - JsonList/JsonMap: static schema vs mixed types
// - 2D arrays: Parquet has no nested array support
.skipIf(o -> o instanceof JsonList || o instanceof
JsonMap
@@ -188,39 +186,33 @@ public abstract class RoundTripTest_Base extends TestBase
{
}
/**
- * Returns true if the object contains structures Parquet cannot
serialize
- * (nested lists, Optional, parent/child loops, etc.).
+ * Returns true if the object contains structures Parquet cannot
serialize due to inherent format limitations:
+ * recursive schemas, annotation-based reconstruction, null beans in
collections, etc.
*/
- // TODO - Figure out how to support these.
private static boolean isParquetIncompatibleBeanOrCollection(Object o) {
if (o == null) return false;
var cls = o.getClass();
var name = cls.getName();
- // Beans with self-referential/cyclic structures, Class-typed fields,
or incompatible DTO structures
- if (name.contains("Classes_RoundTripTest")
- || name.contains("DTOs_RoundTripTest")
- || name.contains("JsonSchema")) // recursive/cyclic structure
- return true;
- // Beans with @NameProperty or @ParentProperty - Parquet
doesn't preserve these annotations
- if (name.contains("NameProperty_RoundTripTest") ||
name.contains("ParentProperty_RoundTripTest"))
+ // JsonSchema has recursive structures (properties that contain
other JsonSchema instances)
+ if (name.contains("JsonSchema"))
return true;
- // Bean with JsonMap constructor - Parquet produces
LinkedHashMap, not JsonMap
- if (name.contains("JsonMaps_RoundTripTest$A"))
+ // Beans with @NameProperty or @ParentProperty - Parquet parser
hits ArrayIndexOutOfBoundsException
+ // in readMapKeyValueColumnChunk due to nested map schema
mismatch
+ if (name.contains("NameProperty_RoundTripTest") ||
name.contains("ParentProperty_RoundTripTest"))
return true;
- // Collection containing Class or other incompatible elements
- if (o instanceof Collection<?> c) {
- boolean hasNull = false;
- Object firstNonNull = null;
- for (var elem : c) {
- if (elem == null) { hasNull = true; continue; }
- if (elem instanceof Class ||
isParquetIncompatibleBeanOrCollection(elem)) return true;
- if (firstNonNull == null) firstNonNull = elem;
+ // Collection containing Class or other incompatible elements
+ if (o instanceof Collection<?> c) {
+ boolean hasNull = false;
+ Object firstNonNull = null;
+ for (var elem : c) {
+ if (elem == null) { hasNull = true; continue; }
+ if (elem instanceof Class ||
isParquetIncompatibleBeanOrCollection(elem)) return true;
+ if (firstNonNull == null) firstNonNull = elem;
+ }
+ // Parquet cannot encode a null bean in a list (no
row-null sentinel in flat schema)
+ if (hasNull && firstNonNull != null &&
isUserDefinedBeanInstance(firstNonNull))
+ return true;
}
- // Parquet cannot encode a null bean in a list (no row-null
sentinel in flat schema)
- // skip collections that mix null elements with non-null
user-defined beans.
- if (hasNull && firstNonNull != null &&
isUserDefinedBeanInstance(firstNonNull))
- return true;
- }
// Map containing Class keys/values or other incompatible
elements
if (o instanceof Map<?, ?> m) {
for (var k : m.keySet())