摘要:创作原由以前觉得文件的读写非常简单,就懒得封装。为了解决上述问题,此框架应运而生。写入文件其中列表构建构建基于注解的测试列表列表你好生成文件内容名称生日你好读取文件测试日志信息你好集合类有时候对象中会包含数组等常见集合。
CSV
基于 java 注解的 csv 读写框架。
相关框架Apache commons-csv
super-csv
简单看了下,这两个框架提供的特性都非常的基础。
创作原由以前觉得 csv 文件的读写非常简单,就懒得封装。
最近一个月写了两次 csv 文件相关的东西,发现要处理的细节还是有的,还浪费比较多的时间。
比如:
UTF-8 中文编码使用 excel 打开乱码,因为缺少 BOM 头。
不同类型字段转化为字符串,顺序的指定,head 头的指定,如果手写都会很繁琐。
读取的时候最后 , 后无元素,split 会缺失等。
为了解决上述问题,此框架应运而生。
特性Fluent 流式写法
基于 java 注解,支持自定义的转换和灵活配置
内置 8 大基本类型以及 String 类型转换
解决 Excel 直接打开,utf-8 乱码问题
支持集合、数组、Map 的存取
支持对象中内嵌其他对象
支持特殊字符转义
变更日志CHANGE_LOG.md
开源地址csv
快速开始 环境jdk7+
maven 3.x
maven 引入示例代码com.github.houbb csv 0.0.6
User.java
演示基本类型的转换
public class User { private String name; private int age; private float score; private double money; private boolean sex; private short level; private long id; private char status; private byte coin; //Getter & Setter & toString() }
对象列表构建
/** * 构建通用测试列表 * @return 列表 */ private List写入buildCommonList() { User user = new User(); short s = 4; byte b = 1; user.age(10) .name("你好") .id(1L) .score(60) .coin(b) .level(s) .money(200) .sex(true) .status("Y"); return Arrays.asList(user); }
测试代码
public void commonTest() { final String path = "src est esourcescommon.csv"; CsvWriteBs.newInstance(path) .write(buildCommonList()); }
文件生成
name,age,score,money,sex,level,id,status,coin 你好,10,60.0,200.0,true,4,1,Y,1读取
public void commonTest() { final String path = "src est esourcescommon.csv"; ListuserList = CsvReadBs.newInstance(path) .read(User.class); System.out.println(userList); }
日志信息
[User{name="你好", age=10, score=60.0, money=200.0, sex=true, level=4, id=1, status=Y, coin=1}]引导类 为什么需要引导类
为了灵活的配置和默认配置并存,使用工具类会大大降低灵活性。
为了用户使用的便利性,和后期拓展的灵活性。
引导类CSV 有两个引导类:
名称 | 作用 |
---|---|
CsvWriteBs | csv 文件写入引导类 |
CsvReadBs | csv 文件读取引导类 |
方法 | 默认值 | 说明 |
---|---|---|
newInstance(final String path) | 必填 | 创建实例,并且指定待写入文件路径。 |
path (final String path) | 配置文件路径,只有重新指定 path 路径时需要调用。 | |
writeHead(boolean writeBom) | true | 是否写入 head 头,如果想指定名称,可以结合注解。只有无 head 信息时,会写入。 |
writeBom(boolean writeBom) | true | 是否写入 UTF8 BOM 头,只有文件为空时才会写入。 |
charset(String charset) | UTF-8 | 指定文件编码 |
sort(ISort sort) | NoSort | 默认不进行字段排序 |
write(List |
无 | 待写入的文件列表 |
escape | false | 是否进行特殊字符的转换 |
方法 | 默认值 | 说明 |
---|---|---|
newInstance(final String path) | 必填 | 创建实例,并且指定待读取文件路径。 |
path (final String path) | 配置文件路径,只有重新指定 path 路径时需要调用。 | |
charset(String charset) | UTF-8 | 指定文件编码 |
sort(ISort sort) | NoSort | 默认不进行字段排序 |
startIndex(int startIndex) | 1 | 文件的第二行,默认第一行是 head |
endIndex(int endIndex) | 文件的最后一行 | |
escape | false | 是否进行特殊字符的转换 |
用于待处理对象的字段上。
/** * 字段显示名称 * 1. 默认使用 field.name * @return 显示名称 */ String label() default ""; /** * 读取是否需要 * @return 是 */ boolean readRequire() default true; /** * 写入是否需要 * @return 是 */ boolean writeRequire() default true; /** * 读取转换 * @return 处理实现类 */ Class extends IReadConverter> readConverter() default CommonReadConverter.class; /** * 写入转换 * @return 处理实现类 */ Class extends IWriteConverter> writeConverter() default StringWriteConverter.class;属性概览表
属性 | 默认值 | 说明 |
---|---|---|
label | 字段名称 | 用于 csv 头生成 |
readRequire | true | 是否需要从 csv 文件读取 |
writeRequire | true | 当前字段是否需要写入 csv 文件 |
readConverter | CommonReadConverter | 将 csv 中的字符串转化为当前字段类型,支持 8 大基本类型+String |
writeConverter | StringWriteConverter | 直接调用当前字段值 toString() 方法,null 直接为空字符串 |
其中 readConverter/writeConverter 支持用户自定义
字段注解 对象定义public class UserAnnotation { @Csv(label = "名称") private String name; @Csv(label = "密码", readRequire = false, writeRequire = false) private String password; @Csv(label = "生日", readConverter = ReadDateConvert.class, writeConverter = WriteDateConvert.class) private Date birthday; //Getter & Setter & toString() }ReadDateConvert/WriteDateConvert
使我们自定义的针对 Date 的转换实现。
Write
public class WriteDateConvert implements IWriteConverter{ @Override public String convert(Date value) { DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); return dateFormat.format(value); } }
ReadDateConvert
public class ReadDateConvert implements IReadConverter写入文件{ @Override public Date convert(String value, Class fieldType) { try { DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); return dateFormat.parse(value); } catch (ParseException e) { throw new RuntimeException(e); } } }
public void annotationTest() { final String path = "src est esourcesannotation.csv"; CsvWriteBs.newInstance(path) .write(buildAnnotationList()); }
其中列表构建:
/** * 构建基于注解的测试列表 * @return 列表 */ private ListbuildAnnotationList() { UserAnnotation user = new UserAnnotation(); user.name("你好") .password("123") .birthday(new Date()); return Arrays.asList(user); }
生成文件内容
名称,生日 你好,20190603读取文件测试
public void annotationTest() { final String path = "src est esourcesannotation.csv"; ListuserList = CsvReadBs.newInstance(path) .read(UserAnnotation.class); System.out.println(userList); }
日志信息
[UserAnnotation{name="你好", password="null", birthday=Mon Jun 03 00:00:00 CST 2019}]集合类
有时候对象中会包含数组、Map、Collection 等常见集合。
为了存储的便利性,默认提供集合的相关支持。
特性和普通字段保持一致,如果指定注解转换,则以注解为准。
使用示例UserCollection.java
用于演示集合的对象
public class UserCollection { private String[] arrays; private LinkedList存储lists; private Map maps; private Set sets; //Getter/Setter/toString() }
待存储对象的构建
/** * 构建基于集合的测试列表 * @return 列表 * @since 0.0.3 */ private ListbuildCollectionList() { UserCollection user = new UserCollection(); String[] arrays = new String[]{"a", "b", "c"}; LinkedList lists = new LinkedList<>(Arrays.asList(arrays)); Map maps = new HashMap<>(); maps.put("key", "value"); maps.put("key2", "value2"); Set sets = new HashSet<>(); sets.add("set1"); sets.add("set2"); user.setLists(lists); user.setArrays(arrays); user.setMaps(maps); user.setSets(sets); return Arrays.asList(user); }
执行存储
public void collectionTest() { final String path = "src est esourcescollection.csv"; CsvWriteBs.newInstance(path) .write(buildCollectionList()); }
存储效果
arrays,lists,maps,sets a|b,a|b|c,key2=value2|key=value,set1|set2读取
测试类
public void collectionTest() { final String path = "src est esourcescollection.csv"; ListuserList = CsvReadBs.newInstance(path) .read(UserCollection.class); System.out.println(userList); }
测试日志
[UserCollection{arrays=[a, b], lists=[a, b, c], maps={key=value, key2=value2}, sets=[set2, set1]}]注意
为了保证 csv 以 , 分隔的统一性。
集合使用 | 进行分隔,其中 map 的 key/value 分隔,用到了 =。
在使用时要注意,不要包含上述的符号,否则会出现解析错乱。
ps: 如果确实用到这些字符,可以见后面的特殊字符转义功能。
支持内嵌对象有时候我们希望像使用 mongoDB 一样,非常方便的存取 csv 的嵌套对象。
对于普通的 csv 都没有实现这个特性,本次做了一个尝试,支持内嵌对象的存取。
取舍就像 csv 的简单,需要用到符号 , 一样。
内嵌对象为了不破坏 csv 的规范,使用了符号 :。
换言之,也就是对象内容中不能使用这个符号。
后期会针对出现的符号进行转义,避免这种冲突。
测试案例 示例对象UserEntry.java
public class UserEntry { /** * 名称 */ private String name; /** * 内嵌的用户信息 */ @CsvEntry private User user; //Getter/Setter/ToString }
这里在需要内嵌的对象上使用注解 @CsvEntry 表示需要进行内嵌的对象转换。
User.java
其中 User 对象是原来使用的普通 java 对象
public class User { private String name; private int age; private float score; private double money; private boolean sex; private short level; private long id; private char status; private byte coin; //Getter/Setter/ToString }写入测试
public void entryTest() { final String path = "src est esourcesentry.csv"; CsvWriteBs.newInstance(path) .write(buildEntryList()); }
buildEntryList()
负责对象构建代码,内容如下:
/** * 用户明细列表 * @return 明细列表 * @since 0.0.5 */ private ListbuildEntryList() { UserEntry userEntry = new UserEntry(); userEntry.name("test"); userEntry.user(buildCommonList().get(0)); return Collections.singletonList(userEntry); }
buildCommonList()
private ListbuildCommonList() { User user = new User(); short s = 4; byte b = 1; user.age(10) .name("你好") .id(1L) .score(60) .coin(b) .level(s) .money(200) .sex(true) .status("Y"); return Arrays.asList(user); }
生成文件效果
name,user test,你好:10:60.0:200.0:true:4:1:Y:1
如你所见,这里内嵌对象的属性使用了 : 进行分隔。
读取测试public void entryTest() { final String path = "src est esourcesentry.csv"; ListuserList = CsvReadBs.newInstance(path) .read(UserEntry.class); System.out.println(userList); }
输出信息
[UserEntry{name="test", user=User{name="你好", age=10, score=60.0, money=200.0, sex=true, level=4, id=1, status=Y, coin=1}}]特殊字符转义
在实际使用中,有时候我们会用到 ,|:=。
这几个被使用的特殊字符。
如果你希望这些特殊的字符被正确的存取,那么可以使用 escape 属性执行。
特殊字符的转换原始 | 转义后 | |
---|---|---|
, | &CSV_COMMA; | |
` | ` | &CSV_OR; |
: | &CSV_COLON; | |
= | &CSV_EUQAL; |
下面演示一下如何使用
暂时转义字符不支持自定义。
测试代码 写入测试public void escapeTest() { final String path = "src est esourcesescape.csv"; CsvWriteBs.newInstance(path) .escape(true) .write(buildUserEscapeList()); }
生成文件效果
name,map,nameList,user one&CSV_COMMA;one,key&CSV_EUQAL;key=value&CSV_EUQAL;value,one&CSV_OR;one|two&CSV_OR;two,entry&CSV_COLON;name:0:0.0:0.0:false:0:0: :0相关代码
UserEscape.java
其中用到的对象为:
public class UserEscape { /** * 使用 , */ private String name; /** * 使用 map = */ private Mapmap; /** * 使用 | */ private List nameList; /** * 使用 : */ @CsvEntry private User user; //Getter & Setter & ToString() }
buildUserEscapeList()
构建时,特意使用了特殊的字符。
private List读取测试buildUserEscapeList() { UserEscape escape = new UserEscape(); Map map = new HashMap<>(); map.put("key=key", "value=value"); User user = new User(); user.name("entry:name"); escape.name("one,one"); escape.nameList(Arrays.asList("one|one", "two|two")); escape.map(map); escape.user(user); return Collections.singletonList(escape); }
public void escapeTest() { final String path = "src est esourcesescape.csv"; ListuserList = CsvReadBs.newInstance(path) .escape(true) .read(UserEscape.class); System.out.println(userList); }
日志信息
[UserEscape{name="one,one", nameList=[one|one, two|two], user=User{name="entry:name", age=0, score=0.0, money=0.0, sex=false, level=0, id=0, status= , coin=0}, map={key=key=value=value}}]后续设计 更丰富的类型支持
支持更多的 java 常见类型。
更灵活的配置比如支持用户自定义转义字符
支持文件的写入模式等等。
开源地址csv
可以查看相关代码。
为了便于其他人阅读和使用,代码拥有详细的注释。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/77856.html
摘要:基于注解生成加签验签。是否写入头,建议第一次写入指定,避免中文乱码指定文件编码默认不进行字段排序无待写入的文件列表方法默认值说明必填创建实例,并且指定待读取文件路径。 csv 基于 java 注解生成加签验签 csv。 开源地址: github csv) 创作原由 以前觉得 csv 文件的多写非常简单,就懒得封装。 最近一个月写了两次 csv 文件相关的东西,发现要处理的细节还是有的,...
摘要:集合类有时候对象中会包含数组等常见集合。为了存储的便利性,默认提供集合的相关支持。特性和普通字段保持一致,如果指定注解转换,则以注解为准。集合使用进行分隔,其中的分隔,用到了。在使用时要注意,不要包含上述的符号,否则会出现解析错乱。 集合类 有时候对象中会包含数组、Map、Collection 等常见集合。 为了存储的便利性,默认提供集合的相关支持。 特性和普通字段保持一致,如果指定注...
摘要:特性支持过程式编程基于字节码的代理重试基于注解的重试,允许自定义注解无缝接入接口与注解的统一解决与中的不足之处设计目的综合了和的优势。基于字节码实现的代理重试,可以不依赖。提供基于代码模式字节码增强实现的方式。 Sisyphus 支持过程式编程和注解编程的 java 重试框架。 特性 支持 fluent 过程式编程 基于字节码的代理重试 基于注解的重试,允许自定义注解 无缝接入 sp...
摘要:在结构上引入了头结点和尾节点,他们分别指向队列的头和尾,尝试获取锁入队服务教程在它提出十多年后的今天,已经成为最重要的应用技术之一。随着编程经验的日积月累,越来越感觉到了解虚拟机相关要领的重要性。 JVM 源码分析之 Jstat 工具原理完全解读 http://click.aliyun.com/m/8315/ JVM 源码分析之 Jstat 工具原理完全解读 http:...
摘要:甲乙交易活动不需要双方见面,避免了双方的互不信任造成交易失败的问题。这就是的核心思想。统一配置,便于修改。带参数的构造函数创建对象首先,就要提供带参数的构造函数接下来,关键是怎么配置文件了。 前言 前面已经学习了Struts2和Hibernate框架了。接下来学习的是Spring框架...本博文主要是引入Spring框架... Spring介绍 Spring诞生: 创建Spring的...
阅读 1330·2021-11-25 09:43
阅读 738·2021-11-18 10:02
阅读 2861·2021-09-07 09:59
阅读 2747·2021-08-30 09:44
阅读 2919·2019-08-30 13:17
阅读 2304·2019-08-29 12:17
阅读 1672·2019-08-28 17:57
阅读 1280·2019-08-26 14:04