如何通过Apache POI更新/评估含REFPROP自定义公式的Excel单元格?
Apache POI无法评估Excel中REFPROP自定义公式的问题解决
问题背景
你遇到的情况很典型:用Apache POI修改Excel单元格数据后,第三方自定义函数REFPROP的公式完全无法被POI评估更新,手动在Excel里操作一切正常,但POI会抛出FormulaParseException错误,提示"未使用的输入"。先再明确下你贴的错误日志:
Exception in thread "main" org.apache.poi.ss.formula.FormulaParseException: Unused input [("Z",D24,"PT","E",D36,D37)] after attempting to parse the formula [[1]!REFPROP("Z",D24,"PT","E",D36,D37)] at org.apache.poi.ss.formula.FormulaParser.parse(FormulaParser.java:2030) at org.apache.poi.ss.formula.FormulaParser.parse(FormulaParser.java:170) at org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook.getFormulaTokens(XSSFEvaluationWorkbook.java:85) at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateAny(WorkbookEvaluator.java:271) at org.apache.poi.ss.formula.WorkbookEvaluator.evaluate(WorkbookEvaluator.java:216) at org.apache.poi.xssf.usermodel.BaseXSSFFormulaEvaluator.evaluateFormulaCellValue(BaseXSSFFormulaEvaluator.java:56) at org.apache.poi.ss.formula.BaseFormulaEvaluator.evaluate(BaseFormulaEvaluator.java:110) at GetCell.evaluateFormulas(GetCell.java:88) at GetCell.update(GetCell.java:70) at Main.main(Main.java:109)
问题根源
REFPROP是Excel的第三方加载项/自定义函数,不属于Excel原生内置函数。Apache POI的公式解析器默认只支持Excel原生函数,它完全不知道REFPROP的参数规则和计算逻辑,所以会把函数里的参数当成无效输入报错,自然没法完成评估。而手动在Excel里正常,是因为Excel加载了REFPROP的插件,能识别并执行这个函数。
解决方案
下面给你三个可行的方案,按推荐程度排序:
方案1:给POI添加REFPROP自定义函数支持(推荐,纯Java实现,跨平台)
Apache POI允许我们扩展公式解析器,添加自定义函数的处理逻辑。步骤如下:
- 实现自定义函数类:
实现FreeRefFunction接口,模拟REFPROP的计算逻辑(如果有REFPROP的Java SDK,直接调用官方API会更准确):import org.apache.poi.ss.formula.functions.FreeRefFunction; import org.apache.poi.ss.formula.eval.*; public class REFPROPFunction implements FreeRefFunction { @Override public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { try { // 解析REFPROP的参数:比如args[0]是属性名("Z"),args[1]是物质编号,args[2]是状态类型("PT")等 String property = getStringVal(args[0]); double substanceId = getNumberVal(args[1]); String stateType = getStringVal(args[2]); // 其他参数同理解析... // 调用REFPROP的计算逻辑(这里替换成你实际的计算代码,比如调用REFPROP Java库) double result = calculateREFPROP(property, substanceId, stateType, ...); return new NumberEval(result); } catch (Exception e) { return ErrorEval.VALUE_INVALID; } } // 辅助方法:把ValueEval转换成字符串 private String getStringVal(ValueEval eval) throws EvaluationException { if (eval instanceof StringEval) { return ((StringEval) eval).getStringValue(); } else if (eval instanceof RefEval) { ValueEval innerEval = ((RefEval) eval).getInnerValueEval(); return getStringVal(innerEval); } throw new EvaluationException(ErrorEval.VALUE_INVALID); } // 辅助方法:把ValueEval转换成数值 private double getNumberVal(ValueEval eval) throws EvaluationException { if (eval instanceof NumberEval) { return ((NumberEval) eval).getNumberValue(); } else if (eval instanceof RefEval) { ValueEval innerEval = ((RefEval) eval).getInnerValueEval(); return getNumberVal(innerEval); } throw new EvaluationException(ErrorEval.VALUE_INVALID); } // 实际的REFPROP计算逻辑 private double calculateREFPROP(String property, double substanceId, String stateType, ...) { // 这里替换成你调用REFPROP API的代码 // 示例:如果用REFPROP的Java库,可能是类似 REFPROP.calculate(property, substanceId, stateType, params) return 0.0; } } - 注册自定义函数到POI:
在创建公式评估器后,把自定义函数注册进去:import org.apache.poi.ss.formula.udf.*; import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator; import java.util.HashMap; import java.util.Map; // 获取工作簿和评估器 XSSFFormulaEvaluator evaluator = (XSSFFormulaEvaluator) workbook.getCreationHelper().createFormulaEvaluator(); // 获取原有自定义函数注册表 UDFFinder existingUdf = evaluator.getUDFFinder(); // 创建自定义函数映射 Map<String, FreeRefFunction> udfMap = new HashMap<>(); udfMap.put("REFPROP", new REFPROPFunction()); // 合并原有注册表和新的自定义函数 UDFFinder newUdfFinder = new AggregatingUDFFinder( existingUdf, new DefaultUDFFinder(udfMap.keySet().toArray(new String[0]), udfMap.values().toArray(new FreeRefFunction[0])) ); // 设置新的注册表到评估器 evaluator.setUDFFinder(newUdfFinder()); - 执行公式评估:
现在再调用评估方法就可以正常处理REFPROP公式了:evaluator.clearAllCachedResultValues(); evaluator.evaluateFormulaCell(getCell("D41")); // 或者批量评估所有公式单元格 evaluator.evaluateAll();
方案2:借助本地Excel进程计算(适合不想实现自定义函数的场景)
如果不想在POI里实现REFPROP的逻辑,可以通过调用本地Excel的COM接口(仅Windows环境),让Excel本身去计算公式:
- 用POI修改完单元格后,保存Excel文件
- 使用Java库(比如Jacob)调用Excel的COM对象,打开文件并触发重新计算,然后保存
- 再用POI重新读取文件,获取更新后的结果
注意:这种方法依赖本地安装的Excel,跨平台性差,但能完全利用Excel对REFPROP的原生支持。
方案3:绕过POI评估,手动触发Excel计算(仅适合非自动化场景)
如果你的流程允许手动介入,可以:
- 用POI修改单元格后保存文件
- 手动打开Excel,让它自动计算REFPROP公式(Excel默认会自动计算)
- 保存文件后,再用POI读取计算后的结果
这种方法简单,但不适合自动化的批量处理场景。
注意事项
- 确保REFPROP的参数格式和Excel里的完全一致,POI对单元格引用、字符串参数的解析要准确
- 如果使用方案1,尽量调用REFPROP官方提供的Java SDK,避免自己实现逻辑出错
- 测试时可以先单独验证自定义函数的参数解析是否正确,再集成到POI中
内容的提问来源于stack exchange,提问作者kryz




