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

Reply via email to