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

exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 4140f38def7 NIFI-15724 Fixed Record CHOICE Type conversion for Arrays 
containing null elements (#11017)
4140f38def7 is described below

commit 4140f38def78b9e2bdfb1aeee04484effb3e13f9
Author: Pierre Villard <[email protected]>
AuthorDate: Wed Mar 18 22:20:42 2026 +0100

    NIFI-15724 Fixed Record CHOICE Type conversion for Arrays containing null 
elements (#11017)
    
    Signed-off-by: David Handermann <[email protected]>
---
 .../serialization/record/util/DataTypeUtils.java   |  2 +-
 .../serialization/record/TestDataTypeUtils.java    | 52 ++++++++++++++++++++++
 .../nifi/json/TestJsonTreeRowRecordReader.java     | 38 ++++++++++++++++
 3 files changed, 91 insertions(+), 1 deletion(-)

diff --git 
a/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/util/DataTypeUtils.java
 
b/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/util/DataTypeUtils.java
index f52314aaaf9..f132be60b9a 100644
--- 
a/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/util/DataTypeUtils.java
+++ 
b/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/util/DataTypeUtils.java
@@ -812,7 +812,7 @@ public class DataTypeUtils {
         if (value instanceof final Object[] array) {
             for (final Object element : array) {
                 // Check each element to ensure its type is the same or can be 
coerced (if need be)
-                if (!isCompatibleDataType(element, elementDataType, strict)) {
+                if (element != null && !isCompatibleDataType(element, 
elementDataType, strict)) {
                     return false;
                 }
             }
diff --git 
a/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/TestDataTypeUtils.java
 
b/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/TestDataTypeUtils.java
index 13bbc096117..da5a33a47fe 100644
--- 
a/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/TestDataTypeUtils.java
+++ 
b/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/TestDataTypeUtils.java
@@ -1242,4 +1242,56 @@ public class TestDataTypeUtils {
         assertFalse(DataTypeUtils.isUUIDTypeCompatible("not-a-uuid"));
         assertFalse(DataTypeUtils.isUUIDTypeCompatible(""));
     }
+
+    @Test
+    void testIsArrayTypeCompatibleWithNullElements() {
+        final DataType intType = RecordFieldType.INT.getDataType();
+        final RecordSchema recordSchema = new SimpleRecordSchema(List.of(
+                new RecordField("db", RecordFieldType.RECORD.getRecordDataType(
+                        new SimpleRecordSchema(List.of(new RecordField("host", 
RecordFieldType.STRING.getDataType())))
+                ))
+        ));
+        final DataType recordType = 
RecordFieldType.RECORD.getRecordDataType(recordSchema);
+
+        assertTrue(DataTypeUtils.isArrayTypeCompatible(new Object[]{null, 42}, 
intType));
+        assertTrue(DataTypeUtils.isArrayTypeCompatible(new Object[]{null, 42}, 
intType, true));
+        assertFalse(DataTypeUtils.isArrayTypeCompatible(new Object[]{null, 
42}, recordType));
+        assertFalse(DataTypeUtils.isArrayTypeCompatible(new Object[]{null, 
42}, recordType, true));
+
+        final MapRecord record = new MapRecord(recordSchema, Map.of("db",
+                new MapRecord(new SimpleRecordSchema(List.of(new 
RecordField("host", RecordFieldType.STRING.getDataType()))),
+                        Map.of("host", "localhost"))));
+        assertTrue(DataTypeUtils.isArrayTypeCompatible(new Object[]{null, 
record}, recordType));
+        assertTrue(DataTypeUtils.isArrayTypeCompatible(new Object[]{null, 
record}, recordType, true));
+        assertFalse(DataTypeUtils.isArrayTypeCompatible(new Object[]{null, 
record}, intType));
+
+        assertTrue(DataTypeUtils.isArrayTypeCompatible(new Object[]{null, 
null}, intType));
+        assertTrue(DataTypeUtils.isArrayTypeCompatible(new Object[]{null, 
null}, recordType));
+        assertFalse(DataTypeUtils.isArrayTypeCompatible(null, intType));
+    }
+
+    @Test
+    void testChooseDataTypeForArrayWithNullElementsInChoice() {
+        final RecordSchema recordSchema = new SimpleRecordSchema(List.of(
+                new RecordField("db", RecordFieldType.RECORD.getRecordDataType(
+                        new SimpleRecordSchema(List.of(new RecordField("host", 
RecordFieldType.STRING.getDataType())))
+                ))
+        ));
+        final DataType arrayOfRecord = 
RecordFieldType.ARRAY.getArrayDataType(RecordFieldType.RECORD.getRecordDataType(recordSchema));
+        final DataType arrayOfInt = 
RecordFieldType.ARRAY.getArrayDataType(RecordFieldType.INT.getDataType());
+        final ChoiceDataType choiceType = (ChoiceDataType) 
RecordFieldType.CHOICE.getChoiceDataType(arrayOfRecord, arrayOfInt);
+
+        final DataType chosen = DataTypeUtils.chooseDataType(new 
Object[]{null, 42}, choiceType);
+        assertNotNull(chosen);
+        assertEquals(RecordFieldType.ARRAY, chosen.getFieldType());
+        assertEquals(RecordFieldType.INT, ((ArrayDataType) 
chosen).getElementType().getFieldType());
+
+        final MapRecord record = new MapRecord(recordSchema, Map.of("db",
+                new MapRecord(new SimpleRecordSchema(List.of(new 
RecordField("host", RecordFieldType.STRING.getDataType()))),
+                        Map.of("host", "localhost"))));
+        final DataType chosenRecord = DataTypeUtils.chooseDataType(new 
Object[]{null, record}, choiceType);
+        assertNotNull(chosenRecord);
+        assertEquals(RecordFieldType.ARRAY, chosenRecord.getFieldType());
+        assertEquals(RecordFieldType.RECORD, ((ArrayDataType) 
chosenRecord).getElementType().getFieldType());
+    }
 }
diff --git 
a/nifi-extension-bundles/nifi-standard-services/nifi-record-serialization-services-bundle/nifi-record-serialization-services/src/test/java/org/apache/nifi/json/TestJsonTreeRowRecordReader.java
 
b/nifi-extension-bundles/nifi-standard-services/nifi-record-serialization-services-bundle/nifi-record-serialization-services/src/test/java/org/apache/nifi/json/TestJsonTreeRowRecordReader.java
index bc5da5edb10..07b41614ed7 100644
--- 
a/nifi-extension-bundles/nifi-standard-services/nifi-record-serialization-services-bundle/nifi-record-serialization-services/src/test/java/org/apache/nifi/json/TestJsonTreeRowRecordReader.java
+++ 
b/nifi-extension-bundles/nifi-standard-services/nifi-record-serialization-services-bundle/nifi-record-serialization-services/src/test/java/org/apache/nifi/json/TestJsonTreeRowRecordReader.java
@@ -904,6 +904,44 @@ class TestJsonTreeRowRecordReader {
         testReadRecords(jsonPath, expected);
     }
 
+    @Test
+    void testChoiceOfArrayTypesWithNullElements() throws Exception {
+        final String inputJson = """
+                {"id":1,"changes":{"config": [{"db": {"host": "old"}}, {"db": 
{"host": "new"}}], "status": [true, false]}}
+                {"id":2,"changes":{"config": [null, 42], "status": [null, 
{"code": 200}]}}
+                """;
+
+        try (final InputStream in = new 
ByteArrayInputStream(inputJson.getBytes(StandardCharsets.UTF_8))) {
+            final RecordSchema schema = inferSchema(in, 
StartingFieldStrategy.ROOT_NODE, null);
+
+            try (final JsonTreeRowRecordReader reader = 
createJsonTreeRowRecordReader(in, schema)) {
+                final Record record1 = reader.nextRecord();
+                assertNotNull(record1);
+                assertEquals(1, record1.getValue("id"));
+
+                final Record changes1 = (Record) record1.getValue("changes");
+                assertNotNull(changes1);
+                final Object[] config1 = (Object[]) 
changes1.getValue("config");
+                assertEquals(2, config1.length);
+                assertInstanceOf(Record.class, config1[0]);
+                assertInstanceOf(Record.class, config1[1]);
+
+                final Record record2 = reader.nextRecord();
+                assertNotNull(record2);
+                assertEquals(2, record2.getValue("id"));
+
+                final Record changes2 = (Record) record2.getValue("changes");
+                assertNotNull(changes2);
+                final Object[] config2 = (Object[]) 
changes2.getValue("config");
+                assertEquals(2, config2.length);
+                assertNull(config2[0]);
+                assertEquals(42, config2[1]);
+
+                assertNull(reader.nextRecord());
+            }
+        }
+    }
+
     @Test
     void testChoiceOfEmbeddedSimilarRecords() throws Exception {
         String jsonPath = 
"src/test/resources/json/choice-of-embedded-similar-records.json";

Reply via email to