java apache poi 关闭小计并且 excel 已损坏

问题描述 投票:0回答:1

我使用 java apache poi 生成数据透视表,当我使用 setOutline(false) 让数据透视表以并行形式而不是大纲形式显示时,它将添加小计行。这很奇怪。然后我尝试使用setDefaultSubtotal(false)禁用小计,然后生成的excel被损坏。当我打开表格时,它指出“我们发现‘pivottable.xlsx’中的某些内容存在问题。您希望我们尽力恢复吗?如果您信任此工作簿的来源,请单击“是是否”” .

代码如下:

@Test
    public void rowLabelTest() {
        try (XSSFWorkbook workbook = new XSSFWorkbook()) {
            XSSFSheet sheet = workbook.createSheet();

            Row row1 = sheet.createRow(0);
            Row row2 = sheet.createRow(1);
            Row row3 = sheet.createRow(2);

            row1.createCell(0).setCellValue("Product");
            row1.createCell(1).setCellValue("Region");
            row1.createCell(2).setCellValue("Sales");

            row2.createCell(0).setCellValue("Product A");
            row2.createCell(1).setCellValue("North");
            row2.createCell(2).setCellValue(100);

            row3.createCell(0).setCellValue("Product B");
            row3.createCell(1).setCellValue("South");
            row3.createCell(2).setCellValue(150);

            XSSFSheet pivotSheet = workbook.createSheet("Pivot Table");
            AreaReference source = new AreaReference("A1:C3", workbook.getSpreadsheetVersion());
            XSSFPivotTable pivotTable = pivotSheet.createPivotTable(source, new CellReference("A5"), sheet);

            int productFieldIndex = 0;
            int regionFieldIndex = 1;

            pivotTable.addRowLabel(productFieldIndex);
            pivotTable.addRowLabel(regionFieldIndex);

            pivotTable.getCTPivotTableDefinition().getPivotFields().getPivotFieldArray(productFieldIndex).setOutline(false);
            pivotTable.getCTPivotTableDefinition().getPivotFields().getPivotFieldArray(regionFieldIndex).setOutline(false);

            CTPivotFields fields = pivotTable.getCTPivotTableDefinition().getPivotFields();
            CTPivotField pivotFieldProduct = fields.getPivotFieldArray(productFieldIndex);
            CTPivotField pivotFieldRegion = fields.getPivotFieldArray(regionFieldIndex);

            if (pivotFieldProduct != null) {
                pivotFieldProduct.setDefaultSubtotal(false);
            }

            if (pivotFieldRegion != null) {
                pivotFieldRegion.setDefaultSubtotal(false);
            }

            try (FileOutputStream fileOut = new FileOutputStream("pivottable.xlsx")) {
                workbook.write(fileOut);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

enter image description here

我尝试删除小计行

excel apache-poi subtotal
1个回答
0
投票

创建数据透视表时,Apache POI 添加与源中的行一样多的“默认”类型 (

<item t="default"/>
) 字段项。这是因为他们没有查看数据,所以他们假设不同值的最大计数。这很好,因为 Excel 在打开时会重建其数据透视缓存。

但是如果我们想要更改默认值,那么这就不行了。那么我们必须知道有哪些项目。

因此,我们至少需要与数据中的不同项目一样多的项目,如编号项目:

<item x="0"/><item x="1"/>
。我们必须构建一个缓存定义,其中包含这些项目的共享元素。

面临的挑战是获取字段的唯一项目来计算所需的项目并从中构建缓存。

你的例子得到了改进:

import java.io.FileOutputStream;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.xssf.usermodel.*;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;

class CreatePivotTableTest {

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

  try (XSSFWorkbook workbook = new XSSFWorkbook(); 
       FileOutputStream fileout = new FileOutputStream("./PivotTable.xlsx") ) {
           
   DataFormatter formatter = new DataFormatter(java.util.Locale.US);
   
   XSSFSheet dataSheet = workbook.createSheet();
   
   Row row;
   Cell cell;
   Object[][] data = new Object[][]{
    new Object[]{"Product", "Region", "Sales"},
    new Object[]{"Product A", "North", 100},
    new Object[]{"Product B", "South", 150}
   };
   for (int r = 0; r < data.length; r++) {
    row = dataSheet.createRow(r);
    Object[] rowData = data[r];
    for (int c = 0; c < rowData.length; c++) {
     cell = row.createCell(c);
     if (rowData[c] instanceof String) {
      cell.setCellValue((String) rowData[c]);
     } else if (rowData[c] instanceof Number) {
      cell.setCellValue(((Number)rowData[c]).doubleValue());
     }
    }
   }

   XSSFSheet pivotSheet = workbook.createSheet("Pivot Table");
   AreaReference source = new AreaReference("A1:C" + data.length, workbook.getSpreadsheetVersion());
   XSSFPivotTable pivotTable = pivotSheet.createPivotTable(source, new CellReference("A5"), dataSheet);

   int productFieldIndex = 0;
   int regionFieldIndex = 1;

   pivotTable.addRowLabel(productFieldIndex);
   pivotTable.addRowLabel(regionFieldIndex);

   /*   
    Apache poi adds as much field items of type "default" (<item t="default"/>) as there are rows in source. 
    This is, because they don't have a look at the data, they are assuming this max count of different values. 
    This is fine because Excel will rebuild its pivot cache while opening. 

    But if we want changing defaults, then this is not fine. Then we must know what items there are.

    So we need at least as much items, as are different ones in the data, as numbered items: <item x="0"/><item x="1"/> 

    And we must build a cache definition which has shared elements for those items.
   */
   
   pivotTable.getCTPivotTableDefinition().getPivotFields().getPivotFieldArray(productFieldIndex).setOutline(false);
   pivotTable.getCTPivotTableDefinition().getPivotFields().getPivotFieldArray(regionFieldIndex).setOutline(false);

   CTPivotFields fields = pivotTable.getCTPivotTableDefinition().getPivotFields();
   CTPivotField pivotFieldProduct = fields.getPivotFieldArray(productFieldIndex);
   CTPivotField pivotFieldRegion = fields.getPivotFieldArray(regionFieldIndex);

   if (pivotFieldProduct != null) {
    //determine unique labels in column
    java.util.TreeSet<String> uniqueItems = new java.util.TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
    for (int r = source.getFirstCell().getRow()+1; r < source.getLastCell().getRow()+1; r++) {
     uniqueItems.add(formatter.formatCellValue(dataSheet.getRow(r).getCell(productFieldIndex)));
    }
    //System.out.println(uniqueItems);

    //build pivot items and cache
    int i = 0;
    for (String item : uniqueItems) {
     //take the items as numbered items: <item x="0"/><item x="1"/>
     pivotFieldProduct.getItems().getItemArray(i).unsetT();
     pivotFieldProduct.getItems().getItemArray(i).setX((long)i);
     //build a cache definition which has shared elements for those items 
     pivotTable.getPivotCacheDefinition().getCTPivotCacheDefinition().getCacheFields().getCacheFieldArray(productFieldIndex)
      .getSharedItems().addNewS().setV(item);
     i++;
    }

    //set pivot field settings
    pivotFieldProduct.setDefaultSubtotal(false); // no subtotals for this field

    //remove further items
    if (pivotFieldProduct.getDefaultSubtotal()) i++; //let one default item be if there shall be subtotals
    for (int k = pivotFieldProduct.getItems().getItemList().size()-1; k >= i; k--) {
     pivotFieldProduct.getItems().removeItem(k);
    }
    pivotFieldProduct.getItems().setCount(i);
   }

   if (pivotFieldRegion != null) {
    //determine unique labels in column
    java.util.TreeSet<String> uniqueItems = new java.util.TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
    for (int r = source.getFirstCell().getRow()+1; r < source.getLastCell().getRow()+1; r++) {
     uniqueItems.add(formatter.formatCellValue(dataSheet.getRow(r).getCell(regionFieldIndex)));
    }
    //System.out.println(uniqueItems);

    //build pivot items and cache
    int i = 0;
    for (String item : uniqueItems) {
     //take the items as numbered items: <item x="0"/><item x="1"/>
     pivotFieldRegion.getItems().getItemArray(i).unsetT();
     pivotFieldRegion.getItems().getItemArray(i).setX((long)i);
     //build a cache definition which has shared elements for those items 
     pivotTable.getPivotCacheDefinition().getCTPivotCacheDefinition().getCacheFields().getCacheFieldArray(regionFieldIndex)
      .getSharedItems().addNewS().setV(item);
     i++;
    }

    //set pivot field settings
    pivotFieldRegion.setDefaultSubtotal(false); // no subtotals for this field

    //remove further items
    if (pivotFieldRegion.getDefaultSubtotal()) i++; //let one default item be if there shall be subtotals
    for (int k = pivotFieldRegion.getItems().getItemList().size()-1; k >= i; k--) {
     pivotFieldRegion.getItems().removeItem(k);
    }
    pivotFieldRegion.getItems().setCount(i);
   }

   workbook.write(fileout);

  }

 }
}
© www.soinside.com 2019 - 2024. All rights reserved.