繁体   English   中英

从 Excel 文件读取数据的更好方法

[英]Better way to read data from Excel file

我必须从一个大约有 40 列的 Excel 文件中读取数据,我正在使用列索引一一读取它。 IE:

Cell cell = row.getCell(0);
if (!(cell == null || cell.getCellType() == Cell.CELL_TYPE_BLANK)) {
            cell.setCellType(Cell.CELL_TYPE_STRING);
            // set in setter
        }

但是这种方法与 Excel 文件的结构紧密结合,因为如果在两者之间添加任何新列,则需要主要代码(列的索引值)。

请建议我从 Excel 文件中读取数据的任何有效方法,该方法应该与 Excel 的结构松散耦合,或者是否有任何其他方式可以提供列与 Java 对象字段的绑定。

我建议添加带有列信息(即名称)的标题行并相应地处理列(即将它们映射到 java 对象)。 也许您甚至可以使用反射 API 来反序列化对象。 类似的东西用于将java对象保存到数据库,我在这里不好,但你可以谷歌并检查。

该标题行可以隐藏在 XL 中。

或者您可以将映射信息放入您的 java 代码中(不修改原始 XL 文件)- 只需为此定义一个数据结构,而不是像row.getCell(0)的硬编码常量-应该更改它以解释您的元数据XL 文件中的列。

换句话说,您将拥有每个您正在处理的 XL 文件的数据定义,以及一个处理每个定义的 XL 文件的通用代码。 您应该有一个将 XL 文件名和定义文件作为参数的例程。

创建了将从 Excel 读取每一行并为每一行创建自定义 Java 对象的实用程序。 请确保在使用前阅读底部的限制。

ExcelUtils.java:

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class ExcelUtils {

    public static <T>  List<T> read(String filePath,Class<T> objClass, Map<String,String> headersToPropertyMap){
         try {
                FileInputStream file = new FileInputStream(new File(filePath));

                //Create Workbook instance holding reference to .xlsx file
                XSSFWorkbook workbook = new XSSFWorkbook(file);
                XSSFSheet sheet = workbook.getSheetAt(0);
                Iterator<Row> rowIterator = sheet.iterator();
                List<T> retList = new LinkedList<T>();
                Constructor<T> constructor =objClass.getConstructor();
                Map<Integer,String> columnIndexToProperty = null;
                if(rowIterator.hasNext()){
                    Row row = rowIterator.next();
                    columnIndexToProperty = getCorrespondingColumnIndex(headersToPropertyMap,row);
                }

                while (rowIterator.hasNext())
                {
                    T obj = constructor.newInstance();
                    Row row = rowIterator.next();
                    setObjectFromRow(obj,row,columnIndexToProperty);
                    retList.add(obj);
                }
                file.close();
                return retList;
            } catch (Exception e) {
                e.printStackTrace();
            }
        return new LinkedList<T>();
    }
    private static <T> void setObjectFromRow(T obj, Row row, Map<Integer,String> columnIndexToProperty){
        int numColumns = row.getPhysicalNumberOfCells();
        for(int i=0;i<numColumns;i++){
            Object value = getCellValue(row.getCell(i));
            ReflectUtils.set(obj, columnIndexToProperty.get(i), value);
        }
    }
    private static Map<Integer,String> getCorrespondingColumnIndex(Map<String,String> headersToPropertyMap,Row row){
        int numColumns = row.getPhysicalNumberOfCells();
        Map<Integer,String> columnIndexToProperty = new HashMap<Integer,String>();
        for(int i=0;i<numColumns;i++){
            Cell cell =row.getCell(i);
            String header = cell.getStringCellValue();
            String property = headersToPropertyMap.get(header);
            if(property==null)
                System.out.println("Warning: not able to find property with header: "+header);
            columnIndexToProperty.put(i, property);
        }
        return columnIndexToProperty;
    }

    private static Object getCellValue(Cell cell ){
        switch (cell.getCellType()) 
        {
            case Cell.CELL_TYPE_NUMERIC:
                return cell.getNumericCellValue();
            case Cell.CELL_TYPE_STRING:
                return cell.getStringCellValue();
            case Cell.CELL_TYPE_BOOLEAN:
                return cell.getBooleanCellValue();
        }
        return null;
    }
}

ReflectUtils.java:

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import com.google.common.base.Optional;

public class ReflectUtils {

    public static boolean set(Object object, String fieldName, Object fieldValue) {
        if(fieldName==null)
            return false;
        Class<?> clazz = object.getClass();
        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                Type pt=null;
                try{
                    pt = field.getGenericType();
                }catch(Exception e){
                    e.printStackTrace();
                }
                if(pt!=null && pt.getTypeName().equals("com.google.common.base.Optional<java.lang.String>"))
                    field.set(object, Optional.fromNullable(fieldValue));
                else if(pt!=null && pt.getTypeName().equals("java.lang.String"))
                    if(fieldValue instanceof Double)
                        field.set(object, String.valueOf(((Double)fieldValue).intValue()));
                    else
                        field.set(object, String.valueOf(fieldValue));
                else if(pt!=null && (pt.getTypeName().equals("java.lang.Integer") || pt.getTypeName().equals("int")))
                    if(fieldValue instanceof Double)
                        field.set(object, ((Double) fieldValue).intValue());
                    else
                        field.set(object, Integer.parseInt(String.valueOf(fieldValue)));
                else
                    field.set(object, fieldValue);
                return true;
            } catch (NoSuchFieldException e) {
                clazz = clazz.getSuperclass();
            } catch (Exception e) {
                throw new IllegalStateException(e);
            }
        }
        return false;
    }
}

用法:

        Map<String,String> headersToPropertyMap = new HashMap<String,String>();
        //The header column name in excel-First, the property you wish to assign the value-firstName
        headersToPropertyMap.put("First", "firstName");
        headersToPropertyMap.put("Last", "lastName");
        headersToPropertyMap.put("Email", "email");
        headersToPropertyMap.put("orgNodeId", "companyname");
        headersToPropertyMap.put("Company Name", "companynameString");
        headersToPropertyMap.put("EULA", "eula");
        headersToPropertyMap.put("Email Notification", "emailNotification");
        return ExcelUtils.read("path to excel file",CUSTOM.class,headersToPropertyMap);

限制:

  • Java 属性仅支持 String、Integer 和 Boolean。
  • 仅支持 excel 中的 String、Double 和 boolean。
  • 必须修改 ReflectUtils 以添加您自己的自定义对象。 例如我添加到 ReflectUtils 中的可选对象。

有两种选择:

  • 第一个/最后一个
  • 迭代器

第一个/最后一个

POI 提供 Sheet.getFirstRowNum()/getLastRowNum() 以便能够从第一行步进到最后一行,并提供 Row.getFirstCellNum()/getLastCellNum() 用于单元格。

请注意,如果未填充某些行/单元格,您仍然可能会遇到空行/单元格。

迭代器

Sheet 和 Row 都实现了 Iterable 接口,所以你可以做类似的事情

for(Row row : sheet) {
     for(Cell cell : row) {
          ...

这允许在不遇到任何空项目的情况下遍历所有可用的行/单元格。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM