java - 如何获取引用单元格范围的 Excel 数据验证下拉值

标签 java excel apache-poi

我在阅读 Excel 文档时偶然发现了一个问题,特别是从单元格中获取下拉值(数据验证)。我能够获得明确定义的值。 enter image description here

我可以通过查看单元格是否在 CellRangeAddress 内来获取值(720x486 等):

    Map<CellRangeAddress, String[]> dropDownValues = new HashMap<>();
    List<? extends DataValidation> dataValidations = sheet.getDataValidations();

    for(DataValidation dataValidation : dataValidations)
    {
        for(CellRangeAddress cellRangeAddress : dataValidation.getRegions().getCellRangeAddresses())
        {
            String[] explicitListValues = dataValidation.getValidationConstraint().getExplicitListValues();
            if(explicitListValues == null)
            {
                continue;
            }
            dropDownValues.put(cellRangeAddress, explicitListValues);
        }
    }

上面的代码只适用于显式值。我看到的问题是在单元格的数据验证源中定义范围时:

enter image description here

sheet.getDataValidations();

不返回有关范围的任何信息或有关数据验证的任何信息。有没有人能够掌握源代码并评估公式以获得值(value)?

最佳答案

我能够检索由 2003 年以后的 Excel 工作表的公式定义的数据验证。

我必须解析 XSSFSheet 以获得特定信息,然后重建和评估公式。

这是我为获得所有 DataValidation 值所做的工作:

Map<CellRangeAddress, String[]> dropDownValues = new HashMap<>();

List<ExtendedDataValidations> extendedDataValidationsList = getExtendedDataValidations(sheet);
for (ExtendedDataValidations extendedDataValidations : extendedDataValidationsList)
{
    AreaReference formulaReference = new AreaReference(extendedDataValidations.formula);
    CellReference[] allReferencedCells = formulaReference.getAllReferencedCells();
    FormulaEvaluator formulaEvaluator = wb.getCreationHelper().createFormulaEvaluator();
    String[] values = new String[allReferencedCells.length];
    for (int j = 0; j < allReferencedCells.length; j++)
    {
        CellReference cellReference = allReferencedCells[j];
        Sheet valueSheet = wb.getSheet(cellReference.getSheetName());
        Cell cell = valueSheet.getRow(cellReference.getRow()).getCell(cellReference.getCol());
        CellValue evaluate = formulaEvaluator.evaluate(cell);
        values[j] = StringUtils.trimToEmpty(StringUtils.removeStart(StringUtils.removeEnd(evaluate.formatAsString(), "\""), "\""));
    }

    String stRef = extendedDataValidations.sqref;
    String[] regions = stRef.split(" ");
    for (String region : regions)
    {
        String[] parts = region.split(":");
        CellReference begin = new CellReference(parts[0]);
        CellReference end = parts.length > 1 ? new CellReference(parts[1]) : begin;
        CellRangeAddress cellRangeAddress = new CellRangeAddress(begin.getRow(), end.getRow(), begin.getCol(), end.getCol());
        dropDownValues.put(cellRangeAddress, values);
    }
}

此外,我为公式和单元格引用定义了一个 Struc。

private static class ExtendedDataValidations
{
    public String formula;
    public String sqref;
}

getExtendedDataValidations 获取工作表中出现数据验证论坛的 CTExtensionList:

public static List<ExtendedDataValidations> getExtendedDataValidations(Sheet sheet)
{
    List<ExtendedDataValidations> extendedDataValidationsList = new ArrayList<>();

    if (sheet instanceof XSSFSheet)
    {
        CTExtensionList extLst = ((XSSFSheet) sheet).getCTWorksheet().getExtLst();
        if (extLst == null)
        {
            return extendedDataValidationsList;
        }

        CTExtension[] extArray = extLst.getExtArray();
        List<Node> dataValidationNodes = new ArrayList<>();
        for (CTExtension anExtArray : extArray)
        {
            searchForDataValidation(anExtArray.getDomNode(), dataValidationNodes);
        }

        for (Node dataValidationNode : dataValidationNodes)
        {
            ExtendedDataValidations dataValidations = new ExtendedDataValidations();
            getDataValidationInfo(dataValidationNode, dataValidations);
            extendedDataValidationsList.add(dataValidations);
        }

    }

    return extendedDataValidationsList;
}

searchForDataValidation 必须遍历工作表的 DOM 节点以查找有关 DataValidation 的特定信息。如果找到将其保存在列表中:

private static void searchForDataValidation(Node node, List<Node> nodesInQuestion)
{
    if (StringUtils.equalsIgnoreCase("x14:dataValidation", node.getNodeName()))
    {
        nodesInQuestion.add(node);
        return;
    }

    for (int i = 0; i < node.getChildNodes().getLength(); i++)
    {
        searchForDataValidation(node.getChildNodes().item(i), nodesInQuestion);
    }
}

getDataValidationInfo 负责获取公式和单元格引用。

private static void getDataValidationInfo(Node node, ExtendedDataValidations dataValidations)
{
    if (StringUtils.equalsIgnoreCase("#text", node.getNodeName()))
    {
        if (StringUtils.equalsIgnoreCase("xm:sqref", node.getParentNode().getNodeName()))
        {
            dataValidations.sqref = node.getNodeValue();
        }
        else if (StringUtils.equalsIgnoreCase("xm:f", node.getParentNode().getNodeName()))
        {
            dataValidations.formula = node.getNodeValue();
        }
        return;
    }

    for (int i = 0; i < node.getChildNodes().getLength(); i++)
    {
        getDataValidationInfo(node.getChildNodes().item(i), dataValidations);
    }
}

可能看起来很复杂,但它确实有效。希望对您有所帮助!

关于java - 如何获取引用单元格范围的 Excel 数据验证下拉值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32235774/

相关文章:

java - XSSF(Apache POI) - 从数据透视表中的单列值添加多列标签

java - 无法使用 Netbeans 从当前目录中的 Java 文件打开文件

java - java oracle 教程中带有锁对象的 Bow/Bower 示例

java - Java 中的大数

java - 该网页在 Spring mvc 中有一个重定向循环

excel - Flume 加载 csv 文件优于 hdfs 接收器

java - 接口(interface)的子类如何被调用

Excel RANDBETWEEN 作为字符串

c# - Excel 电子表格中缺少列

java - apache POI 中单元格的直接寻址