无法在生成 .xlsx 时触发 Pivot Table 计算
Cannot trigger Pivot Table calculation on .xlsx generation
我需要一些帮助来解决我遇到的问题。
我先描述一下我的问题,如果你需要的话,你可以阅读下面的代码并查看实现细节。
简短描述:
我生成了一个 Excel 工作簿,其中包含 2 sheets:
Sheet 1: 通用数据。
Sheet 2:对通用数据进行透视 table。
因为 Apache 提供的一些 POI 被证明是错误的,我通过访问底层 XML 结构创建了 Pivot table。 xlsx 文档。在此我指明了 Pivot table 字段和操作(在本例中为 COUNT)。
我现在正在设计自动 JUnit 测试来验证这一点,这就是我 运行 遇到麻烦的地方。
问题:
生成包含文档的 XLSX 时,数据透视表 table 仅填充值 after 我在客户端中打开它。
我想问一下,在我在客户端打开它之前,是否有办法以编程方式触发数据透视表 Table。
这是 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)
。
如果我先打开文件然后 运行 测试,就不会发生这种情况。
您是否知道一种生成数据透视表 Table 的方法,即数据透视表 table 在生成时计算,或者如何从我的 Java 应用程序中触发它?
我的目标是将这个生成的 xlsx 与已知的 ("golden") 测试 xlsx 逐个单元地进行比较,并验证它们的内容是否相同。
代码示例:
工作簿sheet创建:
private void createSheets(XSSFWorkbook wb) {
generalDataSheet = wb.createSheet(GENERAL_DATA_SHEET_NAME);
pivotTableSheet = wb.createSheet(PIVOT_TABLE_SHEET_NAME);
}
Pivot table实现和使用细节:
// 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);
}
我得到了 PivotTable 的源区域,例如:
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);
}
然后我使用自己的 Pivot table class 来覆盖一些方法:
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 执行的 Pivot table 生成步骤,并允许我使用 Pivot table.
自动生成完整的 Excel
缺点:
。我必须到达 Java 图书馆之外才能执行此操作。
。我必须执行以下额外步骤:opening/saving 并关闭 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();
}
}
我需要一些帮助来解决我遇到的问题。 我先描述一下我的问题,如果你需要的话,你可以阅读下面的代码并查看实现细节。
简短描述: 我生成了一个 Excel 工作簿,其中包含 2 sheets:
Sheet 1: 通用数据。
Sheet 2:对通用数据进行透视 table。
因为 Apache 提供的一些 POI 被证明是错误的,我通过访问底层 XML 结构创建了 Pivot table。 xlsx 文档。在此我指明了 Pivot table 字段和操作(在本例中为 COUNT)。
我现在正在设计自动 JUnit 测试来验证这一点,这就是我 运行 遇到麻烦的地方。
问题: 生成包含文档的 XLSX 时,数据透视表 table 仅填充值 after 我在客户端中打开它。
我想问一下,在我在客户端打开它之前,是否有办法以编程方式触发数据透视表 Table。 这是 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)
。
如果我先打开文件然后 运行 测试,就不会发生这种情况。
您是否知道一种生成数据透视表 Table 的方法,即数据透视表 table 在生成时计算,或者如何从我的 Java 应用程序中触发它?
我的目标是将这个生成的 xlsx 与已知的 ("golden") 测试 xlsx 逐个单元地进行比较,并验证它们的内容是否相同。
代码示例: 工作簿sheet创建:
private void createSheets(XSSFWorkbook wb) {
generalDataSheet = wb.createSheet(GENERAL_DATA_SHEET_NAME);
pivotTableSheet = wb.createSheet(PIVOT_TABLE_SHEET_NAME);
}
Pivot table实现和使用细节:
// 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);
}
我得到了 PivotTable 的源区域,例如:
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);
}
然后我使用自己的 Pivot table class 来覆盖一些方法:
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 执行的 Pivot table 生成步骤,并允许我使用 Pivot table.
自动生成完整的 Excel缺点:
。我必须到达 Java 图书馆之外才能执行此操作。
。我必须执行以下额外步骤:opening/saving 并关闭 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();
}
}