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

时间:2015-10-06 16:07:45

标签: java excel apache-poi

我必须从包含大约40列的Excel文件中读取数据,然后使用列索引逐个读取它。即:

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对象字段的绑定。

3 个答案:

答案 0 :(得分:1)

我建议相应地添加带有列信息(即名称)和进程列(即将它们映射到java对象)的标题行。可能你甚至可以使用反射API来反序列化对象。这样的东西用于将java对象保存到数据库,我不是很好,但你可以google并检查。

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

或者你可以在你的java代码中放置映射信息(不修改原始的XL文件) - 只需为它定义一个数据结构而不是row.getCell(0)中的硬编码常量 - 它应该被改为解释你的meta-有关XL文件中列的数据。

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

答案 1 :(得分:1)

创建了将从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中添加了可选对象。

答案 2 :(得分:0)

有两种选择:

  • 首先/尾
  • 迭代

首先/尾

POI提供了Sheet.getFirstRowNum()/ getLastRowNum(),以便能够从第一行到最后一行,以及Cell的Row.getFirstCellNum()/ getLastCellNum()。

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

迭代

Sheet和Row都实现了Iterable接口,因此您可以执行类似

的操作
for(Row row : sheet) {
     for(Cell cell : row) {
          ...

允许遍历所有可用的行/单元而不会遇到任何空项。