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())

Reply via email to