XmlValueDisconnectedException获取行索引(getRowNum())时

时间:2019-07-25 15:31:56

标签: java exception apache-poi row

在我的代码中,我逐行浏览一个XLSX文件,并使用Apache POI 4.1.0针对数据库对其进行验证。如果发现不正确的行,我会将其添加到List<XSSFRow> toRemove中,以将其“标记”为删除。遍历每一行之后,应该使用这种小方法删除标记为删除的行:

ListIterator<XSSFRow> rowIterator = toRemove.listIterator(toRemove.size());

while (rowIterator.hasPrevious()) {
    XSSFRow row = rowIterator.previous();
    if (row != null && row.getSheet() == sheet) {
        int lastRowNum = sheet.getLastRowNum();
        int rowIndex = row.getRowNum();

        if (rowIndex == lastRowNum) {
            sheet.removeRow(row);
        } else if (rowIndex >= 0 && rowIndex < lastRowNum) {
            sheet.removeRow(row);

        } else {
            System.out.println("\u001B[31mERROR: Removal failed because row " + rowIndex + " is out of bounds\u001B[0m");
        }
        System.out.println("Row " + rowIndex + " successfully removed");
    } else {
        System.out.println("Row skipped in removal because it was null already");
    }
}

但是由于某种未知的原因,它会完美地删除所有行,然后在获取最后一个(第一个添加的)行的行索引(getRowNum())时抛出XmlValueDisconnectedException。

Stacktrace的相关部分:

org.apache.xmlbeans.impl.values.XmlValueDisconnectedException
    at org.apache.xmlbeans.impl.values.XmlObjectBase.check_orphaned(XmlObjectBase.java:1258)
    at org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTRowImpl.getR(Unknown Source)
    at org.apache.poi.xssf.usermodel.XSSFRow.getRowNum(XSSFRow.java:400)
    at Overview.removeRows(Overview.java:122)

编辑:我也尝试更改迭代过程(请参见下文),但错误保持不变。

for (XSSFRow row : toRemove) {
   // same code as above without iterator and while
}

1 个答案:

答案 0 :(得分:1)

如果列表toRemove中包含一行,则该错误会发生。 List允许重复输入。因此,同一行可能会被双重添加到List中。如果这样,Iterator将获得该行的第一个匹配项,并将其从工作表中正确删除。但是,如果稍后再出现同一行,则row.getRowNum()会失败,因为该行在表单中不再存在。

这里是重现该行为的完整代码:

import org.apache.poi.ss.usermodel.*;

import java.io.FileInputStream;
import java.io.FileOutputStream;

import java.util.*;

public class ExcelRemoveRows {

 public static void main(String[] args) throws Exception {

  String filePath = "Excel.xlsx"; // must contain at least 5 filled rows

  Workbook workbook = WorkbookFactory.create(new FileInputStream(filePath));
  Sheet sheet = workbook.getSheetAt(0);

  List<Row> toRemoveList = new ArrayList<Row>();
  toRemoveList.add(sheet.getRow(0));
  toRemoveList.add(sheet.getRow(2));
  toRemoveList.add(sheet.getRow(4));
  toRemoveList.add(sheet.getRow(2)); // this produces the error

  System.out.println(toRemoveList); // contains row hawing index 2 (r="3") two times

  for (Row row : toRemoveList) {
   System.out.println(row.getRowNum()); // XmlValueDisconnectedException on second occurance of row index 2
   sheet.removeRow(row);
  }

  FileOutputStream out = new FileOutputStream("Changed"+filePath);
  workbook.write(out);
  out.close();
  workbook.close();
 }
}

解决方案是避免List多次包含同一行。

我不会收集要在List<XSSFRow>中删除的行,而是要收集在Set<Integer>中要删除的行号。这样可以避免重复,因为Set不允许重复元素。然后只需通过sheet.getRow(rowNum)即可删除要删除的行。

代码:

...
  Set<Integer> toRemoveSet = new HashSet<Integer>();
  toRemoveSet.add(sheet.getRow(0).getRowNum());
  toRemoveSet.add(sheet.getRow(2).getRowNum());
  toRemoveSet.add(sheet.getRow(4).getRowNum());
  toRemoveSet.add(sheet.getRow(2).getRowNum());

  System.out.println(toRemoveSet); // does not contain the row index 2 two times

  for (Integer rowNum : toRemoveSet) {
   Row row = sheet.getRow(rowNum);
   System.out.println(row.getRowNum());
   sheet.removeRow(row);
  }
...