java - 无法在 .xlsx 生成时触发数据透视表计算

标签 java excel pivot-table dynamically-generated

我需要一些帮助来解决我遇到的问题。 我首先将描述我的问题,然后,如果您需要,您可以阅读下面的代码并查看实现细节。

简短描述: 我生成一个包含 2 个工作表的 Excel 工作簿:

表 1:通用数据。

表 2:通用数据的数据透视表。

由于 Apache 提供的一些 POI 被证明存在错误,我通过访问底层 XML 结构创建了数据透视表。 xlsx 文档。在此,我指示数据透视表字段和操作(在本例中为 COUNT)。

我现在正在设计自动 JUnit 测试来验证这一点,这就是我遇到麻烦的地方。

问题: 生成包含文档的 XLSX 时,数据透视表仅在我在客户端中打开它之后填充值。

我想问一下,在客户端中打开数据透视表之前,是否有一种方法可以以编程方式触发数据透视表。 以下是 xlsx 文档底层 xml (pivotTable1.xml) 的 2 个部分:

在 Excel 客户端中打开之前:

<location firstDataCol="1" firstDataRow="1" firstHeaderRow="1" ref="A3:D7"/>
<pivotFields count="8">
<pivotField showAll="false" axis="axisPage">
<items count="8">
<item t="default"/>
<item t="default"/>
<item t="default"/>

在Excel客户端中打开后

<pivotFields count="8">
<pivotField axis="axisPage" showAll="0">
<items count="2">
<item x="0"/>
<item t="default"/>
</items>

如果我尝试在 JUnit 中使用生成的 excel,在打开它之前我会得到一个 NULL: currentWbSheet.getRow(0)。 如果我先打开文件然后运行测试,则不会发生这种情况。

您是否知道一种生成数据透视表的方法,以便在生成时计算数据透视表,或者如何从我的 Java 应用程序触发它?

我的目标是将生成的 xlsx 与已知(“黄金”)测试 xlsx 逐个单元进行比较,并验证它们的内容是否相同。

代码示例: 工作簿工作表创建:

    private void createSheets(XSSFWorkbook wb) {
    generalDataSheet = wb.createSheet(GENERAL_DATA_SHEET_NAME);
    pivotTableSheet = wb.createSheet(PIVOT_TABLE_SHEET_NAME);
}

数据透视表实现和使用详细信息:

// Pivot table constants:
// where the Table starts with the Report Filter field
public static final String PIVOT_TABLE_SOURCE_START = "A1";
// Where the 2nd part of the pivot table starts with the Sum Values field
public static final String PIVOT_TABLE_DATA_START = "A3:B3";
private static final String PIVOT_TABLE_NAME = " Pivot Table";

private static final int INTERFACE_NAME_CELL_POS = 0;
private static final int PROVIDER_NAME_CELL_POS = 4;
private static final int REQUESTER_NAME_CELL_POS = 6;

…
private void populatePivotTableSheet(List<MyDataSet> list) {
//Set position of the pivot table in sheet
    CellReference pivotTableCellPosition = new CellReference(PIVOT_TABLE_SOURCE_START); 
    //set source area for the pivot table
    AreaReference pivotTableSourceArea = getDefaultPivotTableSourceArea(list);
// create pivot table and set attributespivotTable = new PivotTableMyTools(pivotTableSourceArea, PIVOT_TABLE_NAME);
    pivotTable.createPivotTable(pivotTableSheet, pivotTableCellPosition);
    // set the size of the pivot Table - this is because of a bug in regular API
    pivotTable.setRefField(PIVOT_TABLE_DATA_START);
    pivotTable.addRowLabelsField(PROVIDER_NAME_CELL_POS);
    pivotTable.addColumnLabelsField(REQUESTER_NAME_CELL_POS);
    pivotTable.addReportFilterField(INTERFACE_NAME_CELL_POS);
pivotTable.addSumValuesField(DataConsolidateFunction.COUNT,PROVIDER_NAME_CELL_POS);
    }

我获取数据透视表的源区域,例如:

private AreaReference getDefaultPivotTableSourceArea(Object linkSetList) {

List< MyDataSet > list = (List< MyDataSet >) DataSetList;
// construct the target area of the Pivot table
// start cell is calculated as for ex: "General data!A2"
CellReference c1 = new CellReference(GENERAL_DATA_SHEET_NAME + "!" + PIVOT_TABLE_SOURCE_START);
String colName = CellReference.convertNumToColString(COLUMN_HEADERS.length - 1);
// end cell is calculated as for ex: "General data!H5"
CellReference c2 = new CellReference(GENERAL_DATA_SHEET_NAME + "!" + colName + (list.size() + 1));

return new AreaReference(c1, c2);
}

然后我使用自己的数据透视表类来覆盖一些方法:

    public class PivotTableMyTools extends XSSFPivotTable implements IPivotTableMyTools {

    private XSSFSheet pivotTableSheet; // Sheet displaying information in pivot
    private AreaReference sourceDataArea;
    private XSSFPivotTable pivotTable;
    private int numberOfDataFields;
    private String pivotTableName;

public PivotTableMyTools(AreaReference sourceDataArea, String pivotTableName) {

        this.sourceDataArea = sourceDataArea;
        numberOfDataFields = 0;
        this.pivotTableName = pivotTableName;
    }

@Override
public void createPivotTable(XSSFSheet destinationSheet, CellReference pivotTableCellPosition) {

        pivotTableSheet = destinationSheet;
        pivotTable = pivotTableSheet.createPivotTable(sourceDataArea, pivotTableCellPosition);
        pivotTable.getCTPivotTableDefinition().setName(pivotTableName);
    }

// int fieldID is the ID of the field in the list of fields to be added to
// the report (column headers of the source data area)
@Override
public void addReportFilterField(int fieldID) {

    int lastColIndex = getSourceAreaLastColumnIndex();
    // create new pivot field with Column Specifications
    try {
        // throws index out of bounds
        checkColumnIndexOutOfBounds(fieldID, lastColIndex);
        // add pivot field to PivotTable, lastColindex also indicates the
        // number of columns
        addNewPivotField(fieldID, lastColIndex, STAxis.AXIS_PAGE);
        // Columns labels colField should be added.
        addNewCTPageField(fieldID);

    } catch (IndexOutOfBoundsException e) {
        Activator.logInfo("Column index is out of bounds");
        Activator.logError(e.getMessage());
    }

}



private void addNewCTPageField(int columnIndex) {

        CTPageFields pageFields;
        if (pivotTable.getCTPivotTableDefinition().getPageFields() != null) {
            pageFields = pivotTable.getCTPivotTableDefinition().getPageFields();
        } else {
            pageFields = pivotTable.getCTPivotTableDefinition().addNewPageFields();
        }
        // Set the fld and hier attributes
        CTPageField pageField = pageFields.addNewPageField();
        pageField.setFld(columnIndex);
        pageField.setHier(-1);
        // set the count attribute
        pageFields.setCount(pageFields.sizeOfPageFieldArray());

    }



@Override
    public void addRowLabelsField(int columnIndex) {

        pivotTable.addRowLabel(columnIndex);
    }

    @Override
    public void addColumnLabelsField(int columnIndex) {

        int lastColIndex = getSourceAreaLastColumnIndex();
        // create new pivot field with Column Specifications
        try {
            // throws index out of bounds
            checkColumnIndexOutOfBounds(columnIndex, lastColIndex);
            // add pivot field to PivotTable, lastColindex also indicates the
            // number of columns
            addNewPivotField(columnIndex, lastColIndex, STAxis.AXIS_COL);
            // Columns labels colField should be added.
            addNewCTColField(columnIndex);

        } catch (IndexOutOfBoundsException e) {
            Activator.logInfo("Column index is out of bounds");
            Activator.logError(e.getMessage());
        }
    }

    @Override
    public void addSumValuesField(DataConsolidateFunction function, int fieldID) {

        // pivotTable.addColumnLabel(DataConsolidateFunction.COUNT,
        // PROVIDER_NAME_CELL_POS, "Provider count");
        try {
            CTPivotField pivotField = getPivotField(fieldID);
            pivotField.setDataField(true);
        } catch (IndexOutOfBoundsException e) {
            Activator.logInfo("The selected column is out of current range");
            Activator.logError(e.getMessage());
        }

        addNewCTDataField(fieldID, "Count of Provider");

    }



private void addNewCTDataField(int fieldID, String fieldName) {

        numberOfDataFields++;
        CTDataFields dataFields = pivotTable.getCTPivotTableDefinition().addNewDataFields();
        CTDataField dataField = dataFields.addNewDataField();
        dataField.setName(fieldName);
        dataField.setFld(fieldID);
        dataField.setSubtotal(STDataConsolidateFunction.COUNT);
        dataField.setBaseField(0);
        dataField.setBaseItem(0);
        dataFields.setCount(numberOfDataFields);
    }

    private CTPivotField getPivotField(int fieldID) throws IndexOutOfBoundsException {

        CTPivotFields pivotFields = pivotTable.getCTPivotTableDefinition().getPivotFields();
        if (null == pivotFields)
            throw new IndexOutOfBoundsException();
        return pivotFields.getPivotFieldArray(4);
    }

    @Override
    public AreaReference getPivotTableSourceArea() {

        return sourceDataArea;
    }

    @Override
    public int getSourceAreaLastColumnIndex() {

        return (sourceDataArea.getLastCell().getCol() - sourceDataArea.getFirstCell().getCol());
    }

    @Override
    public void setRefField(String pivotTableFieldArea) {

        CTLocation location = pivotTable.getCTPivotTableDefinition().getLocation();
        location.setRef("A3:D7");
    }



/***************** private methods ***********************************/

    private void addNewCTColField(int columnIndex) {

        CTColFields colFields;
        if (pivotTable.getCTPivotTableDefinition().getColFields() != null) {
            colFields = pivotTable.getCTPivotTableDefinition().getColFields();
        } else {
            colFields = pivotTable.getCTPivotTableDefinition().addNewColFields();
        }
        colFields.addNewField().setX(columnIndex);
        colFields.setCount(colFields.sizeOfFieldArray());
    }

    private void addNewPivotField(int columnIndex, int numberOfItems, Enum axisValue) {

        IPivotFieldARTools pivotField = new PivotFieldARTools();
        pivotField.setAxis(axisValue);
        pivotField.createItemsList(numberOfItems);
        pivotField.addToPivotTable(columnIndex, pivotTable);
    }

    private void checkColumnIndexOutOfBounds(int columnIndex, int lastColIndex) throws IndexOutOfBoundsException {

        if (columnIndex > lastColIndex && columnIndex < 0) {
            throw new IndexOutOfBoundsException();
        }
    }

最佳答案

为了解决这个问题,我将创建一个 VBScript 应用程序,我可以将它与我的插件一起发布,并且可以从插件触发。

。该应用程序将执行的操作是:在 Excel 客户端中打开作为参数接收的 Excel 文件,然后保存该文件并关闭客户端。

。这应该会触发 Excel 执行的数据透视表生成步骤,并允许我使用数据透视表自动生成完整的 Excel。

缺点:

。我必须访问外部 Java 库才能执行此操作。

。我必须执行以下额外步骤:打开/保存和关闭 Excel 客户端。 我的 wscript 解决方法的代码如下所示:

excelOpenSave.vbs:

on error resume next

暗淡文件名
文件名 = WScript.Arguments(0)

'WScript.Echo 文件名

Set objExcel = CreateObject("Excel.Application")
objExcel.DisplayAlerts = False
Set objWorkbook = objExcel.Workbooks.Open(filename)
objExcel.Visible = true
objWorkbook.RefreshAll


objExcel.ActiveWorkbook.Save 
objExcel.ActiveWorkbook.Close SaveChanges=True
objExcel.Application.Quit
'WScript.Echo "Finished."


'always deallocate after use...
Set objWorkbook = Nothing
Set objExcel = Nothing
WScript.Quit

我的Java代码是:

public static void triggerOpenSaveCloseScript(String file) {

    String projPath = System.getProperty("user.dir");
    String script = projPath + "\\Script\\excelOpenSave.vbs";
    String command = "CScript " + script + "  " + file;
    int exitValue = 0;
    try {
        Runtime rt = Runtime.getRuntime();
        Process pr = rt.exec(command);
        exitValue = pr.waitFor(); //wait until script finishes
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        System.out.println("CScript exited with error: " + exitValue);
        e.printStackTrace();
    }
}

关于java - 无法在 .xlsx 生成时触发数据透视表计算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36648901/

相关文章:

excel - 按 Excel 或 Google 表格中的值偏移时间单元格

MySQL 选择数据透视表中符合我的条件的行

python - 在 Pandas 中使用数据透视表的加权平均值

java - 从另一个异步方法调用的 Spring 异步方法

Excel 数据验证 - 正则表达式?

java - 为什么简单的代码会加载很多类?

vba - 简化 VBA 以在 Excel 中单击时更改形状颜色

python - 数据透视表不使用数据框中的日期时间索引创建

java - 如何使 Logback 记录一个空行,而不包括模式字符串?

java - 在 Java 中按特定顺序连接 ArrayList