使用方法:
- 提供把实例数据输出到磁盘csv文件的功能
- 提供读取csv文件,并封装成指定实例的功能
- 小工具自己依赖了slf4j+logbak,以及fastJson,如果与系统冲突,可以在pom文件中去除
- 可以自己手动封装jar包,引入到自己的工程,也可以复制CsvUtils.java和CsvConfig.java到工程,直接使用
踩的坑:
反射:
使用反射,创建新实例并给各个属性赋值,获取属性值后,调用set方法赋值,因为获取的值都是String类型,需要根据属性值类型对值转型。
- 一开始希望写一个转类型方法,根据传入的Field的类型,把String类型的值转成属性值,构造的方法类似:
private <T> T tranform(Field field, String value){ switch(field.getType().getSimpleName()){ case "String": ... return (T)xxx; case ... } }
但是出现一个问题,没法转换,一直提示无法把String转换成Object.后来放弃了
- 打算使用反射的方式,使用基础类型的封装类型中的valueOf方法,思路大致是:
- a. 获取Field的类型全称
- b. 根据全称,使用Class.forName()获取对应的Class实例
- c. 根据反射,获取valueOf方法
- d. 执行valueOf方法,把String类型的值转成Field的Type
代码大致是,手打的,不是真正的代码:
String typeName = field.getType.getName(); Class<?> typeClass = Class.forName(typeName); Method valueOf = typeClass.getDeclearMethod("valueOf",java.lang.String); Object obj = typeClass.newInstence(); //这一步出错 valueof.invoke(obj,value); //把要赋予属性的值,由String类型转换成属性类型
但是在获取封装类型的实例是,也就是 Object obj = typeClass.newInstence(); 这一步,提示,无法获取Integer类型的实例,提示是的没有`<init>`什么的,抛出异常的位置是 C:/Program Files/Java/jdk1.8.0_101/src.zip!/java/lang/Class.java:3082 。后来琢磨了一会,发现只有String类型有无参构造方法,其他封装类型都没有无参构造方法,不知道是不是这个原因,String类型可以通过反射获取实例,其他封装类型不能获取实例。
最后放弃挣扎,使用枚举,使用封装类型的valueOf方法对值进行转换。实现方式后面代码。
1、工具实现代码
package com.donfaquir.csv; import com.csvreader.CsvReader; import com.csvreader.CsvWriter; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.charset.Charset; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; /** * @author: * @description: 提供csv文件的操作方法 * @date Create in 2018/12/24 15:01 * @modified By: */ public class CsvUtil { private final Logger log = LoggerFactory.getLogger(CsvUtil.class); /** * 读取csc文件 * * @param filePath 文件路径 * @param clazz 指定类型 * @param csvConfig 配置信息 * @return 读取结果 */ public <T> List<T> readFile(String filePath, Class<T> clazz, CsvConfig csvConfig) throws Exception { if (null == filePath || null == clazz) { return null; } List<T> result = new ArrayList<>(10); this.checkCsvConfig(csvConfig); CsvReader reader = new CsvReader(filePath, csvConfig.getSeparator(), Charset.forName(csvConfig.getCharset())); //获取类方法 Map<Field, Method> methods = new HashMap<>(11); Field[] fields = clazz.getDeclaredFields(); if (fields == null) { log.error("========未获取到类{}的属性值,请确认类{}是否传入正确========", clazz.getName(), clazz.getName()); return null; } for (int i = 0; i < fields.length; i++) { String methodName = "set" + fields[i].getName().substring(0, 1).toUpperCase() + fields[i].getName().substring(1); Method method = clazz.getMethod(methodName, fields[i].getType()); if (method == null) { log.info("====类{}中没有属性{}的set方法,请确定是否实现====", clazz.getName(), fields[i].getName()); continue; } methods.put(fields[i], method); } //跳过头文件 reader.readHeaders(); String[] headers = reader.getHeaders(); if(null == headers || headers.length <=0){ log.error("========文件< {} >缺少数据头,请增加数据头到文件========",filePath); return null; } while (reader.readRecord()) { T obj = (T)clazz.newInstance(); Set<Field> keys = methods.keySet(); for (Field key : keys) { key.setAccessible(true); String value = reader.get(key.getName()); if(StringUtils.isBlank(value)){ continue; } switch (key.getType().getSimpleName()) { case "Integer": methods.get(key).invoke(obj,Integer.valueOf(value)); break; case "String": methods.get(key).invoke(obj,value); break; case "Boolean": methods.get(key).invoke(obj,Boolean.valueOf(value)); break; case "Long": methods.get(key).invoke(obj,Long.valueOf(value)); break; case "Float": methods.get(key).invoke(obj,Float.valueOf(value)); break; case "Double": methods.get(key).invoke(obj,Double.valueOf(value)); break; case "Date": try { methods.get(key).invoke(obj,new SimpleDateFormat(csvConfig.getDateFormat()).parse(value)); break; } catch (ParseException e) { log.info("====日期转换失败,使用的日期格式为:{},与给定的日期数据格式不匹配,请重新指定日期转换格式====",csvConfig.getDateFormat()); log.info("====错误原因:{}",e); throw new RuntimeException(e); } default: methods.get(key).invoke(obj,value); break; } } result.add(obj); } reader.close(); return result; } /** * 读取csc文件 * 默认编码格式是本地编码格式 * @param filePath 文件路径 * @param separator 分隔符 * @param t 指定类型 * @return 读取结果 */ public <T> List<T> readFile(String filePath, char separator, Class<T> t) throws Exception { return this.readFile(filePath,t,this.initCsvConfig()); } /** * 把缓存内容写入到csv文件 * * @param filePath 文件路径 * @param cacheContainer 缓存容器 * @param csvConfig 配置信息 * @return 写入是否成功 */ public <T> boolean writeFile(String filePath, List<T> cacheContainer, Class<T> t,CsvConfig csvConfig) { CsvWriter writer; if (StringUtils.isBlank(filePath) || CollectionUtils.isEmpty(cacheContainer) || null == t) { return false; } this.checkCsvConfig(csvConfig); writer = new CsvWriter(filePath, csvConfig.getSeparator(), Charset.forName(csvConfig.getCharset())); //获取实体类中的属性 Field[] fields = t.getDeclaredFields(); //生成数据头 String[] headers = new String[fields.length]; for (int i = 0; i < fields.length; i++) { headers[i] = fields[i].getName(); } //写入到文件 try { writer.writeRecord(headers); for (T obj : cacheContainer) { String[] str = coverFieldsValue(obj,csvConfig); writer.writeRecord(str); } } catch (Exception e) { e.printStackTrace(); } writer.close(); return true; } /** * 把缓存内容写入到csv文件 * 默认编码格式是本地编码格式 * @param filePath 文件路径 * @param cacheContainer 缓存容器 * @return 写入是否成功 */ public <T> boolean writeFile(String filePath, List<T> cacheContainer, Class<T> t) { return this.writeFile(filePath,cacheContainer,t,this.initCsvConfig()); } /** * 把传入的实例属性的值封装成字符串数组 * * @param obj 实例 * @return 字符串数组 * @throws Exception 异常 */ private <T> String[] coverFieldsValue(T obj,CsvConfig csvConfig) throws Exception { String[] result ; Class<?> clazz = obj.getClass(); Field[] fields = clazz.getDeclaredFields(); if (null == fields || fields.length <= 0) { return null; } result = new String[fields.length]; for (int i = 0; i < fields.length; i++) { new Date(); String methodName = "get" + fields[i].getName().substring(0, 1).toUpperCase() + fields[i].getName().substring(1); Method method = clazz.getMethod(methodName); Object value = method.invoke(obj); if(null == value){ continue; } if("Date".equals(fields[i].getType().getSimpleName())){ Date date = (Date)value; result[i] = new SimpleDateFormat(csvConfig.getDateFormat()).format(date); continue; } result[i] = value.toString(); } return result; } /** * 构造一个默认的配置实例 * 默认编码格式为本地系统编码格式 * @return 设有默认值的配置实例 */ private CsvConfig initCsvConfig(){ String charset = System.getProperty("file.encoding"); return new CsvConfig(charset,"yyyy-MM-dd HH:mm:ss:SSS",','); } /** * 检测给予系统配置信息的完整性 * @param csvConfig 给定的配置实例 */ private void checkCsvConfig(CsvConfig csvConfig){ if(null == csvConfig){ csvConfig = initCsvConfig(); }else{ if(null == csvConfig.getCharset()){ csvConfig.setCharset(System.getProperty("file.encoding")); } if(null == csvConfig.getDateFormat()){ csvConfig.setDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); } //没有指定分隔符 if(0 == csvConfig.getSeparator()){ csvConfig.setSeparator(','); } } } }
2、参数实体类
用于指定编码类型、日期格式和分隔符
package com.donfaquir.csv; /** * @author: * @description: 工具配置类 * @date Create in 2018/12/26 10:09 * @modified By: */ public class CsvConfig { /** 字符编码 */ private String charset; /** 日期格式 */ private String dateFormat; /** 分隔符 */ private char separator; public CsvConfig(){ } public CsvConfig(String charset, String dateFormat, char separator){ this.charset = charset; this.dateFormat = dateFormat; this.separator = separator; } public String getCharset() { return charset; } public void setCharset(String charset) { this.charset = charset; } public String getDateFormat() { return dateFormat; } public void setDateFormat(String dateFormat) { this.dateFormat = dateFormat; } public char getSeparator() { return separator; } public void setSeparator(char separator) { this.separator = separator; } }
3、使用demo
import bean.User; import com.donfaquir.csv.CsvConfig; import com.donfaquir.csv.CsvUtil; import org.apache.commons.collections4.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; /** * @author: * @description: 测试类 * @date Create in 2018/12/24 15:22 * @modified By: */ public class Demo { private static Logger log = LoggerFactory.getLogger(Demo.class); public static void main(String[] args){ String filePath = System.getProperty("user.home")+"/csv_test.csv"; CsvConfig csvConfig = new CsvConfig("gbk","yyyy-MM-dd HH:mm:ss:SSS",','); CsvUtil csvUtil = new CsvUtil(); //写文件代码 /* User user = new User("李磊", "男", 14, new Date(), true, 133443L); User user1 = new User("jack", "M", 22, new Date(), false, 134L); ArrayList<User> users = new ArrayList<>(3); users.add(user); users.add(user1); csvUtil.writeFile(filePath, users, User.class,csvConfig);*/ //读文件代码 try { List<User> objects = csvUtil.readFile(filePath, User.class,csvConfig); if(CollectionUtils.isEmpty(objects)){ log.info("====没有从文件{}获取到值====",filePath); } log.debug("==获取的结果总数:{}",objects.size()); // log.debug("==获取的结果:{}", JSON.toJSONString(objects)); } catch (Exception e) { e.printStackTrace(); } } }
4、使用中用到的domain类
package bean; import java.util.Date; /** * @author: * @description: 用户类 * @date Create in 2018/12/24 15:21 * @modified By: */ public class User { /** 用户名 */ private String name; /** 性别 */ private String sex; /** 年龄 */ private Integer age; /** 生日 */ private Date birthday; /** 信息是否公开 */ private Boolean visible; /** 过车整形id */ private Long longId; public User() { super(); } public User(String name, String sex, Integer age, Date birthday, Boolean visible, Long longId) { this.name = name; this.sex = sex; this.age = age; this.birthday = birthday; this.visible = visible; this.longId = longId; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public Boolean getVisible() { return visible; } public void setVisible(Boolean visible) { this.visible = visible; } public Long getLongId() { return longId; } public void setLongId(Long longId) { this.longId = longId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }