0

I'm trying to create an Excel Pivot with apache poi, but I dont really understand how to create the format that I want. I use this code to create, but I get empty values:

XSSFPivotTable pivotTable = sheet.createPivotTable(aref, pos);
pivotTable.addColumnLabel(DataConsolidateFunction.SUM, 2, colNames[2]);
pivotTable.addColumnLabel(DataConsolidateFunction.SUM, 3, colNames[3]);
pivotTable.addColumnLabel(DataConsolidateFunction.SUM, 0, colNames[0]);
pivotTable.addColumnLabel(DataConsolidateFunction.SUM, 1, colNames[1]);

My data looks like this:

enter image description here

and I want it to look like this:

enter image description here

How do I achieve that?

Axel Richter
  • 56,077
  • 6
  • 60
  • 87

1 Answers1

3

Apache poi has only rudimentary support for creating pivot tables. It only creates default pivot tables but is not able creating special settings without falling back to the underlying ooxml-schemas classes.

For your data source the following code creates what apache poi supports by default:

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

import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.*;

class CreatePivotTableDefault {

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

  try (XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream("ExcelSource.xlsx")); 
       FileOutputStream fileout = new FileOutputStream("ExcelResult.xlsx") ) {

   XSSFSheet dataSheet = workbook.getSheetAt(0);
   XSSFSheet pivotSheet = workbook.createSheet("Pivot");

   AreaReference areaReference = new AreaReference("A1:D5", SpreadsheetVersion.EXCEL2007);

   XSSFPivotTable pivotTable = pivotSheet.createPivotTable(areaReference, new CellReference("A1"), dataSheet);

   pivotTable.addRowLabel(2);
   pivotTable.addRowLabel(3);
   pivotTable.addRowLabel(0);
   pivotTable.addRowLabel(1);

   workbook.write(fileout);

  }

 }
}

This creates a pivot table in outline format, which is the default.

If you wants creating a pivot table which is not in outline format and is not showing subtotals for each field, we need using the underlying ooxml-schemas classes. We need creating the correct pivot table field items. And we need to build a cache definition which has shared elements for those items. See also Apache POI XSSFPivotTable setDefaultSubtotal.

The following code should create the pivot table you wants from your data source:

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

import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPivotField;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.xssf.usermodel.*;

class CreatePivotTable {

 static void addRowLabel(XSSFPivotTable pivotTable, XSSFSheet dataSheet, AreaReference areaReference, int column) { 

  DataFormatter formatter = new DataFormatter(java.util.Locale.US);
  //create row label - apache poi creates as much fields for each as rows are in the pivot table data range
  pivotTable.addRowLabel(column);

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

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

  //set pivot field settings
  ctPivotField.setOutline(false); // no outline format
  ctPivotField.setDefaultSubtotal(false); // no subtotals for this field

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

 }

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

  try (XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream("ExcelSource.xlsx")); 
       FileOutputStream fileout = new FileOutputStream("ExcelResult.xlsx") ) {

   XSSFSheet dataSheet = workbook.getSheetAt(0);
   XSSFSheet pivotSheet = workbook.createSheet("Pivot");

   AreaReference areaReference = new AreaReference("A1:D5", SpreadsheetVersion.EXCEL2007);

   XSSFPivotTable pivotTable = pivotSheet.createPivotTable(areaReference, new CellReference("A1"), dataSheet);

   addRowLabel(pivotTable, dataSheet, areaReference, 2);
   addRowLabel(pivotTable, dataSheet, areaReference, 3);
   addRowLabel(pivotTable, dataSheet, areaReference, 0);
   addRowLabel(pivotTable, dataSheet, areaReference, 1);

   workbook.write(fileout);

  }

 }
}

This code is tested using current apache poi 4.1.2 and needs the full jar of all of the schemas ooxml-schemas-1.4.jar (lower versions for older releases) as mentioned in apache poi FAQ-N10025.

Axel Richter
  • 56,077
  • 6
  • 60
  • 87
  • Wow thanks, I haven't even thought about such an approach. – regis_studios Mar 28 '20 at 16:26
  • I get the following exception and I'm not sure how to fix this: Exception in thread "main" java.lang.NoSuchMethodError: 'org.apache.xmlbeans.XmlObject org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTWorksheetImpl.generatedSetterHelperImpl(org.apache.xmlbeans.XmlObject, javax.xml.namespace.QName, int, short)' – regis_studios Mar 28 '20 at 16:32
  • @regis_studios: This code is tested using current `apache poi 4.1.2` and needs the full jar of all of the schemas `ooxml-schemas-1.4.jar` (lower versions for older releases) as mentioned in [apache poi FAQ-N10025](https://poi.apache.org/help/faq.html#faq-N10025). What `apache poi` version are you using? Make sure, the `ooxml-schemas` jar as well as the `poi-ooxml-schemas jar` fits to your `apache poi` version. – Axel Richter Mar 28 '20 at 16:45
  • I was missing the poi-ooxml-schemas jar. Now it works! – regis_studios Mar 28 '20 at 18:25