【问题标题】:Apache POI XSSFPivotTable setDefaultSubtotalApache POI XSSFPivotTable setDefaultSubtotal
【发布时间】:2026-01-29 18:20:05
【问题描述】:

我对axisRow pivotFields 的setDefaultSubtotal 方法有疑问。当我将 RowLabel 字段的值设置为 false 时,Excel 不喜欢它。在 Excel 中手动设置我想要的方式,然后保存会产生截然不同的 XML,这对于我来说太不同了,无法考虑在幕后进行修复。我不能(到目前为止)操作 XML 来让 POI 电子表格在 Excel 中干净地打开。

实际代码有点长且难以发布,但这里有一个可运行的片段,但存在同样的问题:

private static void sample() throws IOException{
    Workbook wb = new XSSFWorkbook();
    String[][] data = new String[][]{{"STATUS","PASSED","VALUE"},{"BLUE","Y","20"},{"RED","N","10"},{"BLUE","N","30"}};

    XSSFSheet sheet = (XSSFSheet) wb.createSheet("data");
    XSSFSheet pivot = (XSSFSheet) wb.createSheet("summary");
    for(String[] dataRow : data){
        XSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows());
        for(String dataCell : dataRow){
            XSSFCell cell = row.createCell(row.getPhysicalNumberOfCells());
            cell.setCellValue(dataCell);
        }
    }

    XSSFTable table = sheet.createTable();    
    CTTable cttable = table.getCTTable();
    table.setDisplayName("table");
    cttable.setRef("A1:C4");
    cttable.setId(1);

    CTTableColumns columns = cttable.addNewTableColumns();
    columns.setCount(3);

    int i = 1;
    for (String colName : data[0]){
        CTTableColumn column = columns.addNewTableColumn();
        column.setId(++i);
        column.setName(colName);      
    }

    XSSFPivotTable pivotTable =  pivot.createPivotTable(new AreaReference("A1:C4", SpreadsheetVersion.EXCEL2007), new CellReference("A4"), sheet);

    pivotTable.addRowLabel(0);
    CTPivotField fld = pivotTable.getCTPivotTableDefinition().getPivotFields().getPivotFieldList().get(0);
    fld.setOutline(false);

    //fld.setDefaultSubtotal(false); // uncomment and Excel has problems

    pivotTable.addRowLabel(1);
    fld = pivotTable.getCTPivotTableDefinition().getPivotFields().getPivotFieldList().get(1);
    fld.setOutline(false);

    //fld.setDefaultSubtotal(false); // uncomment and Excel has problems

    pivotTable.addColumnLabel(DataConsolidateFunction.COUNT, 2, "test");

    FileOutputStream fileOut = new FileOutputStream("c:/temp/pivotsample.xlsx");
    wb.write(fileOut);
    wb.close();

}

当我生成 POI 版本时,这些是档案中 pivotTable XML 的 location 和 pivotFields 元素:

<location firstDataCol="1" firstDataRow="1" firstHeaderRow="1" ref="F10:G11" colPageCount="1"/>
<pivotFields count="5">
    <pivotField axis="axisRow" showAll="false" compact="true" outline="false" defaultSubtotal="false">
        <items count="4">
            <item t="default"/>
            <item t="default"/>
            <item t="default"/>
            <item t="default"/>
        </items>
    </pivotField>
    <pivotField dataField="true" showAll="false"/>
    <pivotField dataField="true" showAll="false"/>
    <pivotField axis="axisPage" showAll="false">
        <items count="4">
            <item t="default"/>
            <item t="default"/>
            <item t="default"/>
            <item t="default"/>
        </items>
    </pivotField>
    <pivotField axis="axisRow" showAll="false" compact="true" outline="false" defaultSubtotal="false">
        <items count="4">
            <item t="default"/>
            <item t="default"/>
            <item t="default"/>
            <item t="default"/>
        </items>
    </pivotField>
</pivotFields>

当我在 Excel 中打开并允许它恢复工作表时,然后我在 Excel 中进行更改以执行我想要的操作,即两个没有以表格形式小计的行标签,这就是 Excel 保存的内容:

<location ref="F10:I15" firstHeaderRow="1" firstDataRow="2" firstDataCol="2" rowPageCount="1" colPageCount="1"/>
<pivotFields count="5">
    <pivotField axis="axisRow" outline="0" showAll="0" defaultSubtotal="0">
        <items count="3">
            <item x="0"/>
            <item x="1"/>
            <item x="2"/>
        </items>
    </pivotField>
    <pivotField dataField="1" showAll="0"/>
    <pivotField dataField="1" showAll="0"/>
    <pivotField axis="axisPage" showAll="0">
        <items count="3">
            <item x="0"/>
            <item x="1"/>
            <item t="default"/>
        </items>
    </pivotField>
    <pivotField axis="axisRow" outline="0" showAll="0" defaultSubtotal="0">
        <items count="2">
            <item x="0"/>
            <item x="1"/>
        </items>
    </pivotField>
</pivotFields>

我几乎尝试了所有方法,并且了解 Excel 格式,但它依赖于 pivotCacheRecords,因此我最终不得不编写代码来填充它。如果有人可能知道为什么此代码失败,我将不胜感激。

【问题讨论】:

  • 我遇到了同样的问题。你能告诉我你是如何以其他方式解决这个问题的吗?如果您能给我一些示例代码,那就太好了。
  • 对不起,@RajKantaria,我还没有再做这个,现在已经转向其他一些问题,最终会回来的。祝你好运。
  • @RajKantaria,下面的答案工作正常,希望它也适合你。

标签: java excel apache-poi pivot-table


【解决方案1】:

另见How to set default value in Apache POI pivot table report filter

到目前为止,如果枢轴字段用作轴字段,则 apache poi 添加与数据范围中存在的行一样多的“默认”类型的枢轴字段项 (&lt;item t="default"/&gt;)。这是因为他们不想查看数据,因此他们假设与数据中的行一样多的不同值。

这很好,因为 Excel 在打开时会重建其数据透视缓存。但是,如果我们想更改默认值,那就不行了。那么我们必须知道有哪些项目。

所以我们至少需要与数据中不同的项目一样多的项目,作为编号项目:&lt;item x="0"/&gt;&lt;item x="1"/&gt;

我们需要构建一个缓存定义,其中包含这些项目的共享元素。

例子:

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

import java.io.*;

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

class PivotTableTest6 {

 public static void main(String[] args) throws IOException{
    Workbook wb = new XSSFWorkbook();
    String[][] data = new String[][]{{"STATUS","PASSED","VALUE"},{"BLUE","Y","20"},{"RED","N","10"},{"BLUE","N","30"}};

    XSSFSheet sheet = (XSSFSheet) wb.createSheet("data");
    XSSFSheet pivot = (XSSFSheet) wb.createSheet("summary");
    for(String[] dataRow : data){
        XSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows());
        for(String dataCell : dataRow){
            XSSFCell cell = row.createCell(row.getPhysicalNumberOfCells());
            cell.setCellValue(dataCell);
        }
    }

    XSSFTable table = sheet.createTable();    
    CTTable cttable = table.getCTTable();
    table.setDisplayName("table");
    cttable.setRef("A1:C4");
    cttable.setId(1);

    CTTableColumns columns = cttable.addNewTableColumns();
    columns.setCount(3);

    int i = 1;
    for (String colName : data[0]){
        CTTableColumn column = columns.addNewTableColumn();
        column.setId(++i);
        column.setName(colName);      
    }

    XSSFPivotTable pivotTable =  pivot.createPivotTable(new AreaReference("A1:C4", SpreadsheetVersion.EXCEL2007), new CellReference("A4"), sheet);

    pivotTable.addRowLabel(0);
    CTPivotField fld = pivotTable.getCTPivotTableDefinition().getPivotFields().getPivotFieldList().get(0);
    fld.setOutline(false);

    /*   
    Apache poi adds 4 pivot field items of type "default" (<item t="default"/>) here. 
    This is because there are 4 rows (A1:C4) and, because they don't have a look at the data, 
    they are assuming max 4 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.
    */

    for (i = 0; i < 2; i++) {
     //take the first 2 items as numbered items: <item x="0"/><item x="1"/>
     fld.getItems().getItemArray(i).unsetT();
     fld.getItems().getItemArray(i).setX((long)i);
    }
    for (i = 3; i > 1; i--) {
     //remove further items
     fld.getItems().removeItem(i);
    }
    //set new items count
    fld.getItems().setCount(2);

    //build a cache definition which has shared elements for those items 
    //<sharedItems><s v="Y"/><s v="N"/></sharedItems>
    pivotTable.getPivotCacheDefinition().getCTPivotCacheDefinition().getCacheFields().getCacheFieldList().get(0).getSharedItems().addNewS().setV("Y");
    pivotTable.getPivotCacheDefinition().getCTPivotCacheDefinition().getCacheFields().getCacheFieldList().get(0).getSharedItems().addNewS().setV("N");

    fld.setDefaultSubtotal(false);

    pivotTable.addRowLabel(1);
    fld = pivotTable.getCTPivotTableDefinition().getPivotFields().getPivotFieldList().get(1);
    fld.setOutline(false);

    pivotTable.addColumnLabel(DataConsolidateFunction.COUNT, 2, "test");

    FileOutputStream fileOut = new FileOutputStream("pivotsample6.xlsx");
    wb.write(fileOut);
    wb.close();

 }
}

【讨论】:

  • 谢谢您的解释。当我被允许时,我会奖励赏金。在我的具体情况下查看这一点,我将字段值设置为什么似乎并不重要,或者即使我有足够的能力覆盖字段项数组中数据集中的不同值。如果我只是在缓存定义中设置一个值等于源表中该列的第一个条目,会不会有任何不良影响?