最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

EasyExcel實(shí)現(xiàn)讀取excel中的日期單元格并自動(dòng)判定終止讀取

 更新時(shí)間:2024年11月08日 10:20:16   作者:weixin_45614626  
這篇文章主要為大家詳細(xì)介紹了EasyExcel如何實(shí)現(xiàn)讀取excel中的日期單元格并自動(dòng)判定終止讀取,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

個(gè)人在工作中遇到一個(gè)需求,讀取第三方的對(duì)賬文件,但是對(duì)賬文件中的日期的單元格格式不是文本,這樣讀出來(lái)就會(huì)是一個(gè)數(shù)字,表示的是1990年1月1日距今的天數(shù)。而且這個(gè)excel中分成了兩個(gè)表格,第一個(gè)表格是我需要的,第二個(gè)表格不需要讀取,同時(shí)還有說(shuō)明行,表頭不是第一行等約束。

最初樸素的想法是讀取出來(lái)數(shù)字,然后用Date接受,這樣就知道需要excel對(duì)應(yīng)的這串?dāng)?shù)字其實(shí)是日期,然后再自定義的listener里邊定義converter做日期轉(zhuǎn)換。大致代碼如下:

public class CustomModelBuildListener<T> extends AnalysisEventListener<Object> {

    /**
     * head 所在行,用于多head行的情況下, 只想要獲取某行數(shù)據(jù)作為head
     */
    private final int headRow;

    /**
     * data 起始行, 可以跳過(guò)某些不想讀取的數(shù)據(jù),比如示例數(shù)據(jù)
     */
    private final int dataRow;

    /**
     * 最終數(shù)據(jù)存放List
     */
    @Getter
    private final List<T> result = new ArrayList<>();

    /**
     * 用于將原始結(jié)構(gòu)轉(zhuǎn)為指定類型
     */
    private final Class<T> clazz;

    /**
     * 用來(lái)對(duì)應(yīng)字段關(guān)系
     */
    private Map<Integer, ReadCellData<?>> headMap;

    public CustomModelBuildListener(int headRow, int dataRow, Class<T> clazz) {
        this.headRow = headRow;
        this.dataRow = dataRow;
        this.clazz = clazz;
    }

    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        if (getCurrentRow(context) != headRow) {
            return;
        }
        this.headMap = headMap;
        //log.info("Invoke head of listener: {}", JsonUtils.writeValue(headMap));
    }

    @Override
    public void invoke(Object data, AnalysisContext context) {
        int currentRow = getCurrentRow(context);
        if (currentRow < dataRow) {
            return;
        }
        this.result.add(converter(data));
        //log.info("Invoke data of listener, data:{}, row: {}", JsonUtils.writeValue(data), currentRow);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("All analysed done of listener, row: {}", getCurrentRow(context));
    }

    private int getCurrentRow(AnalysisContext context) {
        return context.readRowHolder().getRowIndex() + 1;
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    private T converter(Object data) {
        try {
            Map<Integer, ReadCellData> dataMap = (Map<Integer, ReadCellData>) data;
            Map<String, Object> name2Value = new HashMap<>();
            headMap.forEach((key, value) -> {
                if (!dataMap.containsKey(key)) {
                    return;
                }
                ReadCellData<?> cellData = dataMap.get(key);
                if (cellData.getType() == CellDataTypeEnum.NUMBER) {
                    name2Value.put(value.getStringValue(), cellData.getNumberValue());
                } else if (cellData.getType() == CellDataTypeEnum.STRING) {
                    name2Value.put(value.getStringValue(), cellData.getStringValue());
                }
            });
            T instance = clazz.newInstance();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(ExcelProperty.class)) {
                    ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                    String desc = excelProperty.value()[0];
                    if (name2Value.containsKey(desc)) {
                        field.setAccessible(true);
                        if (field.getType() == Integer.class) {
                            Object value = name2Value.get(desc);
                            if (value instanceof BigDecimal) {
                                field.set(instance, ((BigDecimal) value).intValue());
                            } else if (value instanceof String) {
                                field.set(instance, Integer.parseInt((String) value));
                            } else {
                                log.warn("Unknown type for integer convert, type:{}", value.getClass());
                                throw new RuntimeException("Unknown type for converter, type:" + field.getType());
                            }
                        } else if (field.getType() == Long.class) {
                            Object value = name2Value.get(desc);
                            if (value instanceof BigDecimal) {
                                field.set(instance, ((BigDecimal)value).longValue());
                            } else if (value instanceof String) {
                                field.set(instance, Long.parseLong((String)value));
                            } else {
                                log.warn("Unknown type for long convert, type:{}", value.getClass());
                                throw new RuntimeException("Unknown type for converter, type:" + field.getType());
                            }
                        } else if (field.getType() == Double.class) {
                            Object value = name2Value.get(desc);
                            if (value instanceof BigDecimal) {
                                field.set(instance, ((BigDecimal)value).doubleValue());
                            } else if (value instanceof String) {
                                field.set(instance, Double.parseDouble((String)value));
                            } else {
                                log.warn("Unknown type for double convert, type:{}", value.getClass());
                                throw new RuntimeException("Unknown type for converter, type:" + field.getType());
                            }
                        } else if (field.getType() == String.class) {
                            field.set(instance, name2Value.get(desc));
                        } else if (field.getType() == Date.class) {
                            field.set(instance, TimeUtils.formatExcelDate((Integer) name2Value.get(desc)));
                        } else {
                            //走到這個(gè)邏輯的話,說(shuō)明轉(zhuǎn)換的某些類型沒(méi)有做適配,補(bǔ)齊下就好
                            log.warn("Unknown type for converter, type:{}", field.getType());
                            throw new RuntimeException("Unknown type for converter, type:" + field.getType());
                        }
                    }
                }
            }
            return instance;
        } catch (Exception e) {
            log.error("Convert data error, data:{}", JsonUtils.writeValue(data), e);
            throw new RuntimeException(e);
        }
    }
}

關(guān)鍵就在于最后的field.getType() == Date.class表示了需要把數(shù)字轉(zhuǎn)為日期,但是如果excel里邊本來(lái)就是文本格式,你又用Date接受,也會(huì)走到這個(gè)邏輯里邊,然后就會(huì)報(bào)錯(cuò),因?yàn)檗D(zhuǎn)換方法如下:

public static Date formatExcelDate(int day) {
        LocalDate localDate = LocalDate.ofYearDay(1990, 1);
        localDate = localDate.plusDays(day);
        return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

所以這種寫法只適用于excel是非文本格式的日期,并且接受類屬性是Date。

于是發(fā)現(xiàn)了第二種方法。

點(diǎn)進(jìn)去@ExcelProperty注解,發(fā)現(xiàn)有以下的注釋

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelProperty {

    /**
     * The name of the sheet header.
     *
     * <p>
     * write: It automatically merges when you have more than one head
     * <p>
     * read: When you have multiple heads, take the last one
     *
     * @return The name of the sheet header
     */
    String[] value() default {""};

    /**
     * Index of column
     *
     * Read or write it on the index of column, If it's equal to -1, it's sorted by Java class.
     *
     * priority: index &gt; order &gt; default sort
     *
     * @return Index of column
     */
    int index() default -1;

    /**
     * Defines the sort order for an column.
     *
     * priority: index &gt; order &gt; default sort
     *
     * @return Order of column
     */
    int order() default Integer.MAX_VALUE;

    /**
     * Force the current field to use this converter.
     *
     * @return Converter
     */
    Class<? extends Converter<?>> converter() default AutoConverter.class;

    /**
     *
     * default @see com.alibaba.excel.util.TypeUtil if default is not meet you can set format
     *
     * @return Format string
     * @deprecated please use {@link com.alibaba.excel.annotation.format.DateTimeFormat}
     */
    @Deprecated
    String format() default "";
}

最后一個(gè)format被廢棄了,但是指向了一個(gè)新的注解,是用來(lái)解析時(shí)間轉(zhuǎn)換成Date類型的,

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface DateTimeFormat {

    /**
     *
     * Specific format reference {@link java.text.SimpleDateFormat}
     *
     * @return Format pattern
     */
    String value() default "";

    /**
     * True if date uses 1904 windowing, or false if using 1900 date windowing.
     *
     * @return True if date uses 1904 windowing, or false if using 1900 date windowing.
     */
    BooleanEnum use1904windowing() default BooleanEnum.DEFAULT;
}

于是使用這個(gè)注解,指定format,例如-MM-dd。

但是因?yàn)樾枰付ū眍^行,并且手動(dòng)中斷讀取進(jìn)程,所以還是自定義了一個(gè)listener

public class DefaultModelBuildListener<T> extends AnalysisEventListener<T> {

    /**
     * 最終數(shù)據(jù)存放List
     */
    @Getter
    private List<T> result = new ArrayList<>();

    private boolean continueRead = true;



    @Override
    public void invoke(T data, AnalysisContext context) {
        if (Objects.isNull(data) || isFieldNull(data)) {
            // 停止讀取
            continueRead = false;
            return;
        }
        // 將讀到的數(shù)據(jù)添加到列表中
        result.add(data);
    }

    public static boolean isFieldNull(Object object) {
        if (object == null) {
            return true;
        }

        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                if (field.get(object) == null) {
                    return true;
                } else {
                    return false;
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return false;
    }


    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("All analysed done of listener");
    }

    @Override
    public boolean hasNext(AnalysisContext context) {
        return continueRead;
    }
}

最重要的是判斷終止條件的地方,因?yàn)榈谝粋€(gè)表和第二個(gè)表不一樣,所以第一個(gè)表的列不在第二個(gè)表格中,讀取到第二個(gè)表對(duì)應(yīng)實(shí)體類屬性就是null,所以在invoke方法里加了條件。最初發(fā)現(xiàn)null用的是context.interupt。但是發(fā)現(xiàn)報(bào)異常,點(diǎn)進(jìn)去發(fā)現(xiàn)又是一個(gè)廢棄的方法,提示用hasNext來(lái)終止,進(jìn)一步深入,發(fā)現(xiàn)讀取的處理過(guò)程是:

for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) {
            try {
                if (isData) {
                    readListener.invoke(readRowHolder.getCurrentRowAnalysisResult(), analysisContext);
                } else {
                    readListener.invokeHead(cellDataMap, analysisContext);
                }
            } catch (Exception e) {
                onException(analysisContext, e);
                break;
            }
            if (!readListener.hasNext(analysisContext)) {
                throw new ExcelAnalysisStopException();
            }
        }

每次讀取完一行,會(huì)用hasNext判斷繼不繼續(xù),所以重寫hasNext方法來(lái)無(wú)異常終止即可。

測(cè)試下來(lái)沒(méi)問(wèn)題。

最終兩種讀取方法如下

/**
     * Excel解析方法, 自定義屬性較多,建議優(yōu)先使用parse(InputStream inputStream, Class<T> clazz)
     * @param inputStream 輸入流
     * @param clazz       數(shù)據(jù)模型類的類型
     * @param <T>         數(shù)據(jù)模型的泛型
     * @param headRowNumber 表頭行一共有幾行,這里邊的行都是不會(huì)讀取的
     * @param headRow 表頭行行號(hào)
     * @param dataRow 數(shù)據(jù)行開(kāi)始行數(shù)
     * @return 數(shù)據(jù)列表
     */
    public static <T> List<T> parse(InputStream inputStream, Class<T> clazz, int headRowNumber, int headRow, int dataRow) {
        CustomModelBuildListener<T> dataListener = new CustomModelBuildListener<>(headRow,dataRow, clazz);
        try (InputStream in = inputStream) {
            ExcelReaderBuilder excelReaderBuilder = EasyExcel
                    .read(in, clazz, dataListener)
                    .useDefaultListener(false); //很重要,將不使用ModelBuildEventListener轉(zhuǎn)換對(duì)象
            ExcelReaderSheetBuilder sheetBuilder = excelReaderBuilder.sheet().headRowNumber(headRowNumber);
            sheetBuilder.doRead();
            return dataListener.getResult();
        } catch (Exception e) {
            log.error("Failed to parse excel file", e);
            throw new ServiceException(SystemCode.PARAM_VALID_ERROR, "Failed to parse Excel file "+ e.getMessage());
        }
    }

    /**
     * Excel解析方法, 自定義屬性較多,建議優(yōu)先使用parse(InputStream inputStream, Class<T> clazz)
     * @param inputStream 輸入流
     * @param clazz       數(shù)據(jù)模型類的類型
     * @param <T>         數(shù)據(jù)模型的泛型
     * @param headRowNumber 表頭是第幾行,默認(rèn)下一行就是數(shù)據(jù),如果表頭和數(shù)據(jù)行中間還有不希望讀取的行,需要在listener自定義處理
     * @return 數(shù)據(jù)列表
     */
    public static <T> List<T> parseDefault(InputStream inputStream, Class<T> clazz, int headRowNumber) {
        DefaultModelBuildListener excelDataListener = new DefaultModelBuildListener();
        try (InputStream in = inputStream) {
            EasyExcel.read(in, clazz, excelDataListener)
                    .sheet()
                    .headRowNumber(headRowNumber)
                    .doRead();
            return excelDataListener.getResult();
        } catch (Exception e) {
            log.error("Failed to parse excel file", e);
            throw new ServiceException(SystemCode.PARAM_VALID_ERROR, "Failed to parse Excel file "+ e.getMessage());
        }
    }

第一種方法對(duì)應(yīng)的是parse方法,第二種方案對(duì)應(yīng)的是parseDefault方法。

到此這篇關(guān)于EasyExcel實(shí)現(xiàn)讀取excel中的日期單元格并自動(dòng)判定終止讀取的文章就介紹到這了,更多相關(guān)EasyExcel讀取excel日期單元格內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 關(guān)于web項(xiàng)目讀取classpath下面文件的心得分享

    關(guān)于web項(xiàng)目讀取classpath下面文件的心得分享

    這篇文章主要介紹了關(guān)于web項(xiàng)目讀取classpath下面文件的心得,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 基于java中BlockingQueue的使用介紹

    基于java中BlockingQueue的使用介紹

    本篇文章小編為大家介紹,基于java中BlockingQueue的使用介紹。需要的朋友參考下
    2013-04-04
  • Java實(shí)現(xiàn)插入排序

    Java實(shí)現(xiàn)插入排序

    這篇文章主要介紹了Java實(shí)現(xiàn)插入排序,把一列數(shù)組按從小到大或從大到小排序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • SpringBoot整合Apollo配置中心快速使用詳解

    SpringBoot整合Apollo配置中心快速使用詳解

    本文主要介紹了SpringBoot整合Apollo配置中心快速使用詳解,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • 一文詳解Java中的異常機(jī)制

    一文詳解Java中的異常機(jī)制

    Java的異常處理機(jī)制可以讓程序具有極好的容錯(cuò)性,讓程序更加健壯,這篇文章主要介紹了Java中異常機(jī)制的相關(guān)資料,包括Error和Exception的分類、特點(diǎn)以及異常處理的最佳實(shí)踐,需要的朋友可以參考下
    2025-05-05
  • Java實(shí)現(xiàn)順序表的操作

    Java實(shí)現(xiàn)順序表的操作

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)順序表的基本操作,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • Java單例模式利用HashMap實(shí)現(xiàn)緩存數(shù)據(jù)

    Java單例模式利用HashMap實(shí)現(xiàn)緩存數(shù)據(jù)

    這篇文章主要為大家詳細(xì)介紹了Java單例模式利用HashMap實(shí)現(xiàn)緩存數(shù)據(jù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • spring中在xml配置中加載properties文件的步驟

    spring中在xml配置中加載properties文件的步驟

    這篇文章主要介紹了在spring中如何在xml配置中加載properties文件,本文分步驟給大家介紹在XML配置中加載properties文件的方法,需要的朋友可以參考下
    2023-07-07
  • Java框架MyBatis接口編程過(guò)程解析

    Java框架MyBatis接口編程過(guò)程解析

    這篇文章主要介紹了Java框架MyBatis接口編程過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • hibernate 中 fetch=FetchType.LAZY 懶加載失敗處理方法

    hibernate 中 fetch=FetchType.LAZY 懶加載失敗處理方法

    這篇文章主要介紹了hibernate 中 fetch=FetchType.LAZY 懶加載失敗處理方法,需要的朋友可以參考下
    2017-09-09

最新評(píng)論

临颍县| 祁门县| 吉木萨尔县| 喀什市| 古蔺县| 永康市| 长武县| 南安市| 水富县| 申扎县| 和静县| 淳化县| 温宿县| 临夏市| 祥云县| 革吉县| 乌恰县| 靖远县| 昭通市| 宁明县| 信阳市| 景德镇市| 曲水县| 泸州市| 郧西县| 渑池县| 廊坊市| 东城区| 芒康县| 名山县| 龙泉市| 扎囊县| 淄博市| 肇源县| 仁怀市| 淳安县| 墨脱县| 望江县| 曲松县| 漯河市| 拉孜县|