This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new fc19be11e8 xlsx streaming reader does not handle empty fields
correctly, fixes #7449 (#7472)
fc19be11e8 is described below
commit fc19be11e80d5718235b93df59127424034309f6
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Thu Jul 9 16:24:39 2026 +0200
xlsx streaming reader does not handle empty fields correctly, fixes #7449
(#7472)
---
.../excelinput/staxpoi/StaxPoiSheet.java | 15 +++++++-
.../excelinput/staxpoi/StaxPoiSheetTest.java | 43 ++++++++++++++++++++++
2 files changed, 56 insertions(+), 2 deletions(-)
diff --git
a/plugins/transforms/excel/src/main/java/org/apache/hop/pipeline/transforms/excelinput/staxpoi/StaxPoiSheet.java
b/plugins/transforms/excel/src/main/java/org/apache/hop/pipeline/transforms/excelinput/staxpoi/StaxPoiSheet.java
index 816bccc911..995bda5fa3 100644
---
a/plugins/transforms/excel/src/main/java/org/apache/hop/pipeline/transforms/excelinput/staxpoi/StaxPoiSheet.java
+++
b/plugins/transforms/excel/src/main/java/org/apache/hop/pipeline/transforms/excelinput/staxpoi/StaxPoiSheet.java
@@ -132,14 +132,20 @@ public class StaxPoiSheet implements IKSheet {
event = sheetReader.next();
if (event == XMLStreamConstants.START_ELEMENT
&& sheetReader.getLocalName().equals("is")) {
+ String content = "";
while (sheetReader.hasNext()) {
event = sheetReader.next();
if (event == XMLStreamConstants.CHARACTERS) {
- String content = new
XSSFRichTextString(sheetReader.getText()).toString();
- headerRow.add(content);
+ content = new
XSSFRichTextString(sheetReader.getText()).toString();
+ break;
+ }
+ if (event == XMLStreamConstants.END_ELEMENT
+ && sheetReader.getLocalName().equals("is")) {
+ // empty inline string: stop before consuming the next
cell
break;
}
}
+ headerRow.add(content);
break;
}
}
@@ -245,12 +251,17 @@ public class StaxPoiSheet implements IKSheet {
}
}
if (event == XMLStreamConstants.START_ELEMENT &&
sheetReader.getLocalName().equals("is")) {
+ content = "";
while (sheetReader.hasNext()) {
event = sheetReader.next();
if (event == XMLStreamConstants.CHARACTERS) {
content = new
XSSFRichTextString(sheetReader.getText()).toString();
break;
}
+ if (event == XMLStreamConstants.END_ELEMENT
+ && sheetReader.getLocalName().equals("is")) {
+ break;
+ }
}
}
if (event == XMLStreamConstants.START_ELEMENT &&
sheetReader.getLocalName().equals("f")) {
diff --git
a/plugins/transforms/excel/src/test/java/org/apache/hop/pipeline/transforms/excelinput/staxpoi/StaxPoiSheetTest.java
b/plugins/transforms/excel/src/test/java/org/apache/hop/pipeline/transforms/excelinput/staxpoi/StaxPoiSheetTest.java
index 9fa58878a1..8838e30803 100644
---
a/plugins/transforms/excel/src/test/java/org/apache/hop/pipeline/transforms/excelinput/staxpoi/StaxPoiSheetTest.java
+++
b/plugins/transforms/excel/src/test/java/org/apache/hop/pipeline/transforms/excelinput/staxpoi/StaxPoiSheetTest.java
@@ -367,6 +367,49 @@ class StaxPoiSheetTest {
assertEquals(KCellType.STRING_FORMULA, rowCells[1].getType());
}
+ // An inline string cell whose text is empty (e.g. <is><t></t></is>) must be
read as an empty
+ // value in its own column, and must NOT swallow the content of the
following cell. This is what
+ // the streaming Excel writer produces for empty string fields; a broken
reader shifted every
+ // column after the empty one to the left.
+ private static final String SHEET_INLINE_EMPTY_CELL =
+ String.format(
+ BP_SHEET,
+ "<dimension ref=\"A1:D2\"/>"
+ + "<sheetData>"
+ + "<row r=\"1\">"
+ + "<c r=\"A1\" t=\"inlineStr\"><is><t>colA</t></is></c>"
+ + "<c r=\"B1\" t=\"inlineStr\"><is><t>colB</t></is></c>"
+ + "<c r=\"C1\" t=\"inlineStr\"><is><t>colC</t></is></c>"
+ + "<c r=\"D1\" t=\"inlineStr\"><is><t>colD</t></is></c>"
+ + "</row>"
+ + "<row r=\"2\">"
+ + "<c r=\"A2\" t=\"inlineStr\"><is><t>valA</t></is></c>"
+ + "<c r=\"B2\" t=\"inlineStr\"><is><t></t></is></c>"
+ + "<c r=\"C2\" t=\"inlineStr\"><is><t>valC</t></is></c>"
+ + "<c r=\"D2\" t=\"inlineStr\"><is><t>valD</t></is></c>"
+ + "</row>"
+ + "</sheetData>");
+
+ @Test
+ void testInlineStringEmptyCellDoesNotShiftColumns() throws Exception {
+ final String sheetId = "1";
+ final String sheetName = "Sheet 1";
+ XSSFReader reader =
+ mockXSSFReader(
+ sheetId,
+ SHEET_INLINE_EMPTY_CELL,
+ mock(SharedStringsTable.class),
+ mock(StylesTable.class));
+ StaxPoiSheet spSheet = new StaxPoiSheet(reader, sheetName, sheetId);
+
+ IKCell[] row = spSheet.getRow(1);
+ // The empty cell (colB) must keep its column; colC/colD must not shift
left.
+ assertEquals("valA", row[0].getValue());
+ assertEquals("", row[1].getValue(), "empty inline string cell must be an
empty value in colB");
+ assertEquals("valC", row[2].getValue(), "colC must not be shifted into
colB");
+ assertEquals("valD", row[3].getValue());
+ }
+
// The row and column bounds of all cells in the worksheet are specified in
ref attribute of
// Dimension tag in sheet
// xml