记一次使用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;
}

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

java opencsv解析csv文件的更多相关文章

  1. php解析.csv文件

    public function actionImport() { //post请求过来的 $fileName = $_FILES['file']['name']; $fileTmpName = $_F ...

  2. JAVA简便解析json文件

    JAVA简便解析json文件 首先放上我要解析的json文件: { "resultcode":"200", "reason":"S ...

  3. [cocos2dx utils] cocos2dx读取,解析csv文件

    在我们的游戏中,经常需要将策划的数值配置成csv文件,所以解析csv文件就是一个很common的logic, 例如如下csv文件: 下面是一个基于cocos2dx 2.2.4的实现类: #ifndef ...

  4. Qt解析CSV文件

    最近需要解析Excel文件,于是顺带写了解析CSV的代码 定义数据类型LX::Sheet #ifndef LX_H #define LX_H #include <QString> #inc ...

  5. .NET 上传并解析CSV文件存库

    1.前端: 放置浏览按钮 <div class="row inner_table text-center"> <input id="fileId&quo ...

  6. Java jdom解析xml文件带冒号的属性

    Java jdom解析xml文件带冒号的属性 转载请标明出处: https://dujinyang.blog.csdn.net/article/details/99644824 本文出自:[奥特曼超人 ...

  7. 如何用Java解析CSV文件

    首先看一下csv文件的规则: csv(Comma Separate Values)文件即逗号分隔符文件,它是一种文本文件,可以直接以文本打开,以逗号分隔.windows默认用excel打开.它的格式包 ...

  8. 解析 csv文件 java ***最爱那水货

    /** * csv文件解析 <br> * wx 微信明细数据 第1行是标题 ,最后2行 是总结 提取数据需要过滤<br> * zfb 支付宝明细数据 前4行 和最后4行是总结 ...

  9. selenium3 文件系列之------ opencsv读取csv文件

    最近在学习selenium有关文件的读取测试,今天先总结一下如何读取csv文件.CSV的定义是与逗号分隔的值(Comma-Separated Values),在Java中需要用到第三方lib去处理读取 ...

随机推荐

  1. elasticSearch插件的安装以及使用nginx的modles收集nginx的日志

    1.首先在windows环境上搭建es的集群 集群的配置如下 #node01的配置: cluster.name: es-itcast-cluster node.name: node01 node.ma ...

  2. git 远程分支和tag标签的操作

    git远程分支操作:1.创建远程分支git push --set-upstream origin develop:develop2在服务器创建远程分支devlop2,让本地的develop分支和dev ...

  3. 重学 Java 设计模式:实战迭代器模式「模拟公司组织架构树结构关系,深度迭代遍历人员信息输出场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 相信相信的力量! 从懵懂的少年,到拿起键盘,可以写一个Hell ...

  4. 吉比特&雷霆游戏--2020春招实习

    笔试 题量较大,仅记了一些印象比较深刻的题. 题型为选择 + 填空(给C++代码填输出结果) + 编程 编程题不会太难,最难的就一道字符串的全排列(类似剑指offer第38题LeetCode链接)可以 ...

  5. python 3内置函数

    2018-07-14 enumerate() 用于指定下标 例: m = ['a','b','c'] for i,j in enumerate(m,1): print(i,j) 输出: 1 a 2 b ...

  6. python之浅谈循环

    目录 流程控制之while循环 语法 while+break while+continue while+else while循环的嵌套 流程控制之for循环 语法 for+break for+cont ...

  7. tkinter的控件

    包含如下控件: Button  按钮控件 Canvas 画布控件,显示图形元素如线程或文本 Checkbutton 多选框控件,用于在程序中提供多项选择框 Entry 单行输入控件,用于显示简单的文本 ...

  8. C++ 不定参数"..."的使用

    #include <iostream> #include <cstdio> #include <cstdarg> #define LEN_MSG_BUFFER 12 ...

  9. 08 . Kubernetes之 ingress及Ingress Controller

    Ingress简介 ingress是什么? 上篇文章介绍service时有说了暴露了service的三种方式ClusterIP.NodePort与LoadBalance,这几种方式都是在service ...

  10. Fetch方法封装、业务实践

    说Fetch之前啊,我们不得不说一说Ajax了,以前使用最多的自然是jQuery封装的Ajax方法了,强大而且好用. 有人说了,jQuery的Ajax都已经封装得那么好了,你还整Fetch干什么,这不 ...