英文:
Apache POI's FormulaEvaluator doesn't work after modifying a Cell's formula
问题
我正在使用Apache POI来写入电子表格中的一些测试用例预期值...
目前,这些预期值都以字符串单元格值的形式存在。它们包含一些期望的美元金额...
这是一个示例行:
会员ID | NPI编号 | 会员前缀 | 会员名字 | 会员姓氏 | 会员后缀 | 支付频率(A,M,S,Q) | 是否为子会员? | 期望费用 | 期望频率折扣 | 期望家庭折扣 | 期望净额 | 期望总分期 | 期望净分期 | 测试用例步骤2通过? |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
139 | 1209360364 | Main07-28-2023 01:28:49 | RandomMember | A | true | $ 2,362.00 | $ 326.00 | $ 116.91 | $1,919.09 | $ 2,362.00 | $ 1,919.09 | true |
我确保美元金额采用“会计数字格式”。
我注意到需要让Excel计算期望净额(一个单元格减去其他一些单元格的总和),而不是通过我的代码,通过输入以下公式来实现:
=I34-(J34+K34)
这在电子表格中可以工作:按下回车键后,该值会被计算。但是,当我尝试以编程方式读取它时,我遇到了一些问题:
这个实用方法:
public static double GetDollarAmount(Cell cell) {
switch (cell.getCellType()) {
case CellType.NUMERIC.code:
return cell.getNumericCellValue();
case CellType.STRING.code:
return SMDNumberUtils.parseDouble(cell.getStringCellValue());
case CellType.FORMULA.code:
return cell.getSheet().getWorkbook()
.getCreationHelper()
.createFormulaEvaluator()
.evaluate(cell)
.getNumberValue();
default:
throw new IllegalStateException("Cell Type " + cell.getCellTypeEnum().name() + " not yet supported!");
}
}
对于公式类型的单元格失败,因为它返回0,而不是在示例行的情况下返回1919.09。当我检查evaluate()
调用返回的CellValue
时,我看到一个带有#VALUE!
的CellValue
(通常在尝试在字符串值上使用数值公式时会抛出)。
即使我尝试调整我的代码以重新编写公式,通过在任何单元格引用周围包装“VALUE()”:
public static double GetDollarAmount(Cell cell) {
switch (cell.getCellType()) {
case CellType.NUMERIC.code:
return cell.getNumericCellValue();
case CellType.STRING.code:
return SMDNumberUtils.parseDouble(cell.getStringCellValue());
case CellType.FORMULA.code:
FormulaEvaluator evaluator = cell.getSheet().getWorkbook()
.getCreationHelper()
.createFormulaEvaluator();
CellValue cellValue = evaluator.evaluate(cell);
if (!cellValue.getCellTypeEnum().equals(CellType.ERROR))
return cellValue.getNumberValue();
cell.setCellFormula(cell.getCellFormula()
.replaceAll("[A-Z]+\\d+", match -> "VALUE(" + match + ")"))
return evaluator.evaluate(cell)
.getNumberValue();
default:
throw new IllegalStateException("Cell Type " + cell.getCellTypeEnum().name() + " not yet supported!");
}
}
它仍然失败,出现相同的问题!除了下一次运行我们有一个安全的公式外,什么都没有改变!
我该如何修复这个问题,考虑到我的测试框架Katalon Studio将我困在Apache POI v3.17上?
英文:
I am using Apache POI to write to a spreadsheet some test case expectations...
These expectations, as of right now, are all in the form of string cell values. They contain some expected dollar amounts...
Here's example row:
Member ID | NPI Number | Member Prefix | Member First Name | Member Last Name | Member Suffix | Payment Frequency (A, M, S, Q) | Is Child Member? | Expected Fee | Expected Frequency Discount | Expected Family Discount | Expected Net Amount | Expected Gross Installment | Expected Net Installment | Test Case Step 2 Passed? |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
139 | 1209360364 | Main07-28-2023 01:28:49 | RandomMember | A | true | $ 2,362.00 | $ 326.00 | $ 116.91 | $1,919.09 | $ 2,362.00 | $ 1,919.09 | true |
I make sure that the dollar amounts have the Accounting Number Format.
I did notice a need to have Excel calculate an Expected Net Amount (one cell minus the sum of some others), instead of my code, by entering in a formula like:
=I34-(J34+K34)
This works in the spreadsheet: after hitting enter, the value is calculated. However, when I try to read it programmatically, I face some issue:
The util method:
public static double GetDollarAmount(Cell cell) {
switch (cell.getCellType()) {
case CellType.NUMERIC.code:
return cell.getNumericCellValue();
case CellType.STRING.code:
return SMDNumberUtils.parseDouble(cell.getStringCellValue());
case CellType.FORMULA.code:
return cell.getSheet().getWorkbook()
.getCreationHelper()
.createFormulaEvaluator()
.evaluate(cell)
.getNumberValue();
default:
throw new IllegalStateException("Cell Type ${cell.getCellTypeEnum().name} not yet supported!")
}
}
fails for the formula-type cell, because it returns 0 instead of, in the case of that example row, 1919.09 . When I check the CellValue
that the evaluate()
call returns, I see a CellValue
with #VALUE!
(which typically gets thrown when you're tryna use a numeric formula on string values)
Even when I try to tweak my code to re-write the formula, by wrapping "VALUE()" around any cell references:
public static double GetDollarAmount(Cell cell) {
switch (cell.getCellType()) {
case CellType.NUMERIC.code:
return cell.getNumericCellValue();
case CellType.STRING.code:
return SMDNumberUtils.parseDouble(cell.getStringCellValue());
case CellType.FORMULA.code:
FormulaEvaluator evaluator = cell.getSheet().getWorkbook()
.getCreationHelper()
.createFormulaEvaluator();
CellValue cellValue = evaluator.evaluate(cell);
if (!cellValue.getCellTypeEnum().equals(CellType.ERROR))
return cellValue.getNumberValue();
cell.setCellFormula(cell.getCellFormula()
.replaceAll(/[A-Z]+\d+/, { match ->
return "VALUE($match)"
}))
return evaluator.evaluate(cell)
.getNumberValue();
default:
throw new IllegalStateException("Cell Type ${cell.getCellTypeEnum().name} not yet supported!")
}
}
It still fails, with the same issue! Nothing changes, except we have a safe formula for next run!
How can I fix this, given that my testing framework, Katalon Studio, has me stuck on Apache POI v3.17 ?
答案1
得分: 0
我能够通过对代码进行一些调整来解决这个问题:
public static double GetDollarAmount(Cell cell) {
switch (cell.getCellType()) {
case CellType.NUMERIC.code:
return cell.getNumericCellValue();
case CellType.STRING.code:
return SMDNumberUtils.parseDouble(cell.getStringCellValue());
case CellType.FORMULA.code:
CellValue cellValue = this.EvaluateCellFormula(cell);
if (!cellValue.getCellTypeEnum().equals(CellType.ERROR))
return cellValue.getNumberValue();
cell.setCellFormula(cell.getCellFormula()
.replaceAll(/[A-Z]+\d+/, { match ->
return "VALUE($match)"
}))
return this.EvaluateCellFormula(cell)
.getNumberValue();
default:
throw new IllegalStateException("Cell Type ${cell.getCellTypeEnum().name} not yet supported!")
}
}
public static CellValue EvaluateCellFormula(Cell cell) {
return cell.getSheet().getWorkbook()
.getCreationHelper()
.createFormulaEvaluator()
.evaluate(cell)
}
看起来我们每次调用evaluate()
时都需要一个新的FormulaEvaluator
...
英文:
I was able to fix this problem, with some tweaking of the code:
public static double GetDollarAmount(Cell cell) {
switch (cell.getCellType()) {
case CellType.NUMERIC.code:
return cell.getNumericCellValue();
case CellType.STRING.code:
return SMDNumberUtils.parseDouble(cell.getStringCellValue());
case CellType.FORMULA.code:
CellValue cellValue = this.EvaluateCellFormula(cell);
if (!cellValue.getCellTypeEnum().equals(CellType.ERROR))
return cellValue.getNumberValue();
cell.setCellFormula(cell.getCellFormula()
.replaceAll(/[A-Z]+\d+/, { match ->
return "VALUE($match)"
}))
return this.EvaluateCellFormula(cell)
.getNumberValue();
default:
throw new IllegalStateException("Cell Type ${cell.getCellTypeEnum().name} not yet supported!")
}
}
public static CellValue EvaluateCellFormula(Cell cell) {
return cell.getSheet().getWorkbook()
.getCreationHelper()
.createFormulaEvaluator()
.evaluate(cell)
It looks like we need new FormulaEvaluator
every time we evaluate()
...
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论