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

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

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

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

发表评论

匿名网友

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

确定