2

I am having an issue with the setDefaultSubtotal method for axisRow pivotFields. When I set the value to false for a RowLabel field Excel doesn't like it. Manually setting things up how I want them in Excel and then saving produces dramatically different XML, too different for me to contemplate fixing behind the scenes. I can't (so far) manipulate the XML to get the POI spreadsheet to open cleanly in Excel.

The actual code is a little long and unwieldy to post, but here is a runnable piece with the same problem:

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();

}

When I generate the POI version, these are the location and pivotFields elements of the pivotTable XML inside the archive:

<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>

When I open in Excel and allow it to recover the sheet, I then make the changes in Excel to do what I'd like, namely two row labels without subtotals in tabular form, this is what Excel saves:

<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>

I have tried just about everything, and I understand the Excel format, but it depends on pivotCacheRecords, so I'd end up having to write code to populate that. If there's anyone who can maybe see why this code fails, I'd appreciate a pointer.

John Kuhns
  • 506
  • 4
  • 20
  • I am getting the same issue. Can you tell me how you solved this in other way. It will be great if you give me some sample code. – Raj Kantaria Jul 08 '16 at 04:44
  • Sorry, @RajKantaria, I haven't done any more with this, have turned to some other issues for now, will come back to it eventually. Good luck. – John Kuhns Jul 08 '16 at 14:18
  • @RajKantaria, the answer below works fine, hope it does for you as well. – John Kuhns Nov 08 '16 at 14:58

1 Answers1

5

See also How to set default value in Apache POI pivot table report filter.

Until now apache poi adds as much pivot field items of type "default" (<item t="default"/>) as rows are present in the data range, if the pivot fields where used as axis fields. This is because they don't want to have a look at the data, and so they are assuming as much different values as rows are in the data.

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 need to build a cache definition which has shared elements for those items.

Example:

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();

 }
}
Community
  • 1
  • 1
Axel Richter
  • 56,077
  • 6
  • 60
  • 87
  • Thank you, and thanks for the explanation. I will award the bounty when I'm allowed to. On looking at this in my specific case it doesn't seem to matter what I set the field values to or even if I have enough to cover the distinct values from the data set in the field item array. Would there be any ill effects if I just set one value in the cache definition equal to the first entry for that column in the source table? – John Kuhns Nov 08 '16 at 14:08