Apache POI的FormulaEvaluator在修改单元格公式后不起作用。

huangapple go评论74阅读模式
英文:

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 ->
					    return "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()...

huangapple
  • 本文由 发表于 2023年8月9日 13:55:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76864925.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定