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";