Java 使用Apache Poi重命名XSSFTable的标头会导致XLSX文件损坏

Java 使用Apache Poi重命名XSSFTable的标头会导致XLSX文件损坏,java,excel,apache-poi,xssf,Java,Excel,Apache Poi,Xssf,我正在尝试重命名现有xlsx文件的标题。其想法是使用excel文件将数据从XML导出到excel,并在用户进行调整后重新导入XML 目前,我们已经用Excel创建了一个“模板”xlsx工作表,其中已经包含一个可排序表(poi中的XSSFTable)和一个到XSD源的映射。然后我们通过POI导入它,将XML数据映射到其中并保存它。要根据用户调整工作表,我们需要将现有表的标题/列名翻译成不同的语言。它与POI 3.10-FINAL配合使用,但由于升级到4.0.1,因此在打开时会导致xlsx文件损坏

我正在尝试重命名现有xlsx文件的标题。其想法是使用excel文件将数据从XML导出到excel,并在用户进行调整后重新导入XML

目前,我们已经用Excel创建了一个“模板”xlsx工作表,其中已经包含一个可排序表(poi中的XSSFTable)和一个到XSD源的映射。然后我们通过POI导入它,将XML数据映射到其中并保存它。要根据用户调整工作表,我们需要将现有表的标题/列名翻译成不同的语言。它与POI 3.10-FINAL配合使用,但由于升级到4.0.1,因此在打开时会导致xlsx文件损坏

我已经在stackoverflow上找到了这个问题 但它没有回答,而且很老。但我试图弄清楚这些评论可能是关于什么的,并试图将现有的XSSFTable展平,将填充的数据复制到新的工作表中,然后将新的XSSFTable放在数据上。不幸的是,这似乎是相当复杂的,所以我回来纠正破碎的标题单元格。 我还试图用POI创建整个工作表,并不再使用“模板”-xslx,但我不知道如何实现XSD映射(在Excel中,开发人员工具->源->添加节点,然后将节点映射到动态表中的某些单元格)

在poi升级之前一直有效的代码基本上如下所示:

//工作表是当前的XSSFSheet
//header是将模板中的原始标头名称映射到新翻译名称的映射
//HeaderRownNumber是包含要转换的tableheader的行
公共静态无效平移标头(图纸、最终地图标头、int headerrownumber){
CellRangeAddress=new CellRangeAddress(HeaderRownNumber,HeaderRownNumber,0,sheet.getRow(HeaderRownNumber).getLastCellNum());//Cellrange是标题行
迈塞尔沃克细胞沃克=新迈塞尔沃克(表,地址);
遍历(新的CellHandler(){
公共void onCell(Cell Cell,CellWalkContext ctx){
String val=cell.getStringCellValue();
if(标题容器(val)){
cell.setCellValue(header.get(val));
}
}
});
}
MyCellWalk是一个org.apache.poi.ss.util.cellwalk.cellwalk,它从左上角到右下角遍历单元格范围

据我所知,仅仅更改单元格的平面值是不够的,因为xlsx在一些映射中保留了对cellname的引用,但我不知道如何获取它们并重命名标题。也许还有另一种方法来翻译headernames?

好吧,如果apache-poi不会失败的话,就应该这样做

以下所有操作都是使用ApachePOI4.0.1来完成的

我已下载了您的
dummy_template.xlsx
,然后尝试更改工作表中的表列标题。但是,即使在调用
XSSFTable.updateHeaders
之后,
XSSFTable
中的列名也没有改变。所以我调查了一下,以确定为什么不会发生这种情况。我们发现:

if (row != null && row.getCTRow().validate()) {
 //do changing the column names
}
因此,仅当工作表中的对应行根据
Office Open XML
名称空间有效时,才会更改列名。但在后来的
Excel
版本(2007年之后)中,添加了额外的名称空间。在这种情况下,行的
XML
如下所示:

<row r="4" spans="1:3" x14ac:dyDescent="0.25">
如果我使用
Excel2007
打开
dummy\u template.xlsx
,然后另存为
dummy\u template2007.xlsx
,则行的
XML
将更改为


现在,当使用此
dummy_template2007.xlsx时,无需手动调用
XSSFTable.updateHeaders
。由
XSSFTable.commit调用的。commit
会自动执行此操作。

如您所知,问题在于
XSSFTable
中的标题不仅是工作表中的文本内容,而且是
XSSFTableColumn
s的名称,并作为列说明符,如
[ColumnName]
结构化引用的部分。因此,当
XSSFTable
的标题需要更改时,所有这些都需要更改。首先,我要尝试的是,调用以及在表中更改标题单元格值后,是否解决了问题。我已经尝试了UpdateHeader和updateReferences,但遗憾的是,它们没有起作用。那张桌子打开时仍被弄脏了。是否有任何提示,我可以在哪里找到,哪些结构化引用需要更改?“是否有任何提示,我可以在哪里找到,哪些结构化引用需要更改?”:没有,这里没有“模板”xlsx文件。有这么多不同的可能性。我上传了一个虚拟xlsx,这基本上就是我的项目所用的:在谷歌硬盘上。有一个XSD映射到表中,并且位于“reference”后面的顶部。除了更新所有参考资料之外,我也乐于接受其他解决方法,但我现在真的没有主意了。我现在正在努力解决这个问题,我真的感到很尴尬。非常感谢!这解决了我的问题-我自己从来没有猜到这一点…多么棘手!
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.ss.util.cellwalk.*;

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

import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;

import java.io.*;
import java.util.*;

class ExcelRenameTableColumns {

 static void translateHeaders(Sheet sheet, final Map<String,String> header, int headerrownumber) {
  CellRangeAddress address = new CellRangeAddress(
   headerrownumber, headerrownumber, 
   0, sheet.getRow(headerrownumber).getLastCellNum());

  CellWalk cellWalk = new CellWalk (sheet, address);
  cellWalk.traverse(new CellHandler() {
   public void onCell(Cell cell, CellWalkContext ctx) {
    String val = cell.getStringCellValue();
    if (header.containsKey(val)) {
     cell.setCellValue(header.get(val));
    }
   }
  });
 }

 static void updateHeaders(XSSFTable table) {
  XSSFSheet sheet = (XSSFSheet)table.getParent();
  CellReference ref = table.getStartCellReference();

  if (ref == null) return;

  int headerRow = ref.getRow();
  int firstHeaderColumn = ref.getCol();
  XSSFRow row = sheet.getRow(headerRow);
  DataFormatter formatter = new DataFormatter();

System.out.println(row.getCTRow().validate()); // false!

  if (row != null /*&& row.getCTRow().validate()*/) {
   int cellnum = firstHeaderColumn;
   CTTableColumns ctTableColumns = table.getCTTable().getTableColumns();
   if(ctTableColumns != null) {
    for (CTTableColumn col : ctTableColumns.getTableColumnList()) {
     XSSFCell cell = row.getCell(cellnum);
     if (cell != null) {
      col.setName(formatter.formatCellValue(cell));
     }
     cellnum++;
    }
   }
  }
 }

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

  String templatePath = "dummy_template.xlsx";
  String outputPath = "result.xlsx";

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

  Map<String, String> header = new HashMap<String, String>();
  header.put("textone", "Spalte eins");
  header.put("texttwo", "Spalte zwei");
  header.put("textthree", "Spalte drei");

  translateHeaders(sheet, header, 3);

  XSSFTable table = ((XSSFSheet)sheet).getTables().get(0);

  updateHeaders(table);

  FileOutputStream outputStream = new FileOutputStream(outputPath);
  workbook.write(outputStream);
  outputStream.close();
  workbook.close();

 }
}