zoukankan      html  css  js  c++  java
  • java opencsv解析csv文件

    记一次使用opencsv解析csv文件时碰到的坑

    最近在开发过程中需要解析csv文件,公司用的解析工具是opencsv,在根据opencsv的官方文档去解析时发现csv文件中含有繁体字,使用其自带的CsvToBean来转换会出现异常com.opencsv.exceptions.CsvRequiredFieldEmptyException: Number of data fields does not match number of headers.于是我这里想到的方法是使用CsvReader来读取文件,然后通过反射来注入到bean中,这里做个记录希望对大家有帮助

    一、引入依赖包

    <dependency>
        <groupId>com.opencsv</groupId>
        <artifactId>opencsv</artifactId>
        <version>4.4</version>
    </dependency>

    二、具体代码

    1.自定义注解,基础一点的就是只需要定义数据列标题名title、格式转换convert,我这里是由于业务需要所以稍微复杂些

    import java.lang.annotation.*;
    
    /**
     * <p>
     * 解析csv文件注解
     * </p>
     *
     * @Author zlc0w01
     * @Date 2020/4/20 10:15
     * @Version 1.0
     */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CsvReadColmn {
        /**
         * 该列数据的标题名
         */
        String title();
    
        /**
         * 是否需要加密
         */
        boolean encrypt() default false;
    
        /**
         * 读csv文件字段绑定
         * 绑定格式转换类,字符串转Object
         * @return
         */
        Class<? extends AbstractConvertCsvBase> convert() default AbstractConvertCsvBase.Converter.class;
    
        /**
         * 转换依赖字段,如有某个字段转换需要依赖其他字段,
         * 可设置为依赖字段的title
         * @return
         */
        String convertRelyColumn() default "";
    
    }

    2.接下来就是定义用于转换的基类了,这里面可以自己定义,我这里定义的意思是转换所有字段,去掉前面的单引号“'”,其他需要定义的转换规则可以继承这个类,然后重写convert方法就行了

    public abstract class AbstractConvertCsvBase {
        private static final String SPLIT = "'";
    
        /**
         * 转换
         * @param params 参数中必须有key为"value"
         * @return
         */
        public Object startConvert(Map<String,String> params){
            String value = params.get("value");
            if (StringUtils.isNotBlank(value) && SPLIT.equals(value.substring(0,1))){
                value = value.substring(1);
            }
            if (StringUtils.isBlank(value)){
                return null;
            }
            params.put("value",value);
            return convert(params);
        }
    
        /**
         * 转换方法
         * @param params
         * @return
         */
        public abstract Object convert(Map<String,String> params);
    
        public static class Converter extends AbstractConvertCsvBase{
            public static Converter newInstance() {
                return new Converter();
            }
            @Override
            public Object convert(Map<String,String> params) {
                return params.get("value");
            }
        }
    }

    3.定义需要转换的bean

    public class GbInsurancePolicy implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        /**
         * id
         */
        @CsvReadColmn(title = "内部号码")
        private String id;
    
        @CsvReadColmn(title = "出生日期",convert = CsvConvertStringToSimpleDate.class)
        private Date birthday;
    
    }

    //上面说到定义转换规则,这里拿出生日期举例
    public class CsvConvertStringToSimpleDate extends AbstractConvertCsvBase {

    @SneakyThrows
    @Override
    public Object convert(Map<String, String> params) {
    String value = params.get("value");
    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
    return sf.parse(value);
    }
    }

    4.定义反射来解析csv文件

    public List<T> readSpecialCsv(String filePath) throws Exception{
            List<T> list = new ArrayList<>();
            FileInputStream fr = new FileInputStream(filePath);
            UnicodeInputStream unicodeInputStream = new UnicodeInputStream(fr, true);
            String enc = unicodeInputStream.getEncodingFromStream();
            InputStreamReader is = new InputStreamReader(unicodeInputStream, enc);
            CSVReader reader = new CSVReader(is);
            String [] nextLine;
            String[] header = reader.readNext();
            while ((nextLine = reader.readNext()) != null) {
                if (nextLine.length < header.length){
                    continue;
                }
                T t = getTClass().newInstance();
                Field[] fields = t.getClass().getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    Field fieldName = t.getClass().getDeclaredField(field.getName());
                    CsvReadColmn csvReadColmn = fieldName.getAnnotation(CsvReadColmn.class);
                    if (null != csvReadColmn){
                        int columnPosition = Arrays.asList(header).indexOf(csvReadColmn.title());
                        String value = nextLine[columnPosition];
                        AbstractConvertCsvBase convert = csvReadColmn.convert().newInstance();
                        Map<String,String> params = new HashMap<>();
                        params.put("value",value);
                        //是否有需要依赖某个字段来转换的
                        if (StringUtils.isNotBlank(csvReadColmn.convertRelyColumn())){
                            int relyColumnPosition = Arrays.asList(header).indexOf(csvReadColmn.convertRelyColumn());
                            String relyColumn = nextLine[relyColumnPosition];
                            params.put("relyColumn",relyColumn);
                        }
                        Object obj = convert.startConvert(params);
                        String methodName = "set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
                        Method m = t.getClass().getDeclaredMethod(methodName, fieldName.getType());
                        m.invoke(t, obj);
                    }
                }
                list.add(t);
            }
            reader.close();
            return list;
        }

    以上就是整个流程,希望对大家有帮助

  • 相关阅读:
    执行truncate引发ORA-02266的问题分析
    一文搞懂MySQL-8.0 redo优化
    写给迷茫中的大一大二的学弟学妹,学渣逆袭中的个人经历与心得
    99+好友共同关注,公众号推荐
    Cesium
    Cesium
    Cesium
    Cesium
    虚拟化学习笔记-KVM虚拟化跨机迁移原理
    虚拟化学习笔记-KVM虚拟化跨机迁移原理
  • 原文地址:https://www.cnblogs.com/yilichentu/p/12768312.html
Copyright © 2011-2022 走看看