资讯专栏INFORMATION COLUMN

基于 java 注解的 csv 读写框架更加简单灵活

includecmath / 1172人阅读

摘要:创作原由以前觉得文件的读写非常简单,就懒得封装。为了解决上述问题,此框架应运而生。写入文件其中列表构建构建基于注解的测试列表列表你好生成文件内容名称生日你好读取文件测试日志信息你好集合类有时候对象中会包含数组等常见集合。

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";
    List userList = 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 文件读取引导类
CsvWriteBs
方法 默认值 说明
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 list) 待写入的文件列表
escape false 是否进行特殊字符的转换
CsvReadBs
方法 默认值 说明
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 是否进行特殊字符的转换
Csv 注解 注解属性说明

用于待处理对象的字段上。

    /**
     * 字段显示名称
     * 1. 默认使用 field.name
     * @return 显示名称
     */
    String label() default "";

    /**
     * 读取是否需要
     * @return 是
     */
    boolean readRequire() default true;

    /**
     * 写入是否需要
     * @return 是
     */
    boolean writeRequire() default true;

    /**
     * 读取转换
     * @return 处理实现类
     */
    Class readConverter() default CommonReadConverter.class;

    /**
     * 写入转换
     * @return 处理实现类
     */
    Class 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 List buildAnnotationList() {
    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";
     List userList = 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 List buildCollectionList() {
    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";
    List userList = 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 List buildEntryList() {
    UserEntry userEntry = new UserEntry();
    userEntry.name("test");
    userEntry.user(buildCommonList().get(0));
    return Collections.singletonList(userEntry);
}

buildCommonList()

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);
}

生成文件效果

name,user
test,你好:10:60.0:200.0:true:4:1:Y:1

如你所见,这里内嵌对象的属性使用了 : 进行分隔。

读取测试
public void entryTest() {
    final String path = "src	est
esourcesentry.csv";
    List userList = 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 Map map;

    /**
     * 使用 |
     */
    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";
    List userList = 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

相关文章

  • 基于 java 注解 csv 文件读写框架

    摘要:基于注解生成加签验签。是否写入头,建议第一次写入指定,避免中文乱码指定文件编码默认不进行字段排序无待写入的文件列表方法默认值说明必填创建实例,并且指定待读取文件路径。 csv 基于 java 注解生成加签验签 csv。 开源地址: github csv) 创作原由 以前觉得 csv 文件的多写非常简单,就懒得封装。 最近一个月写了两次 csv 文件相关的东西,发现要处理的细节还是有的,...

    Gemini 评论0 收藏0
  • CSV-03- csv 读写框架支持数组、Map、Collection 等常见集合

    摘要:集合类有时候对象中会包含数组等常见集合。为了存储的便利性,默认提供集合的相关支持。特性和普通字段保持一致,如果指定注解转换,则以注解为准。集合使用进行分隔,其中的分隔,用到了。在使用时要注意,不要包含上述的符号,否则会出现解析错乱。 集合类 有时候对象中会包含数组、Map、Collection 等常见集合。 为了存储的便利性,默认提供集合的相关支持。 特性和普通字段保持一致,如果指定注...

    leiyi 评论0 收藏0
  • spring retry, guava retrying 整合-sisyphus java 重试框

    摘要:特性支持过程式编程基于字节码的代理重试基于注解的重试,允许自定义注解无缝接入接口与注解的统一解决与中的不足之处设计目的综合了和的优势。基于字节码实现的代理重试,可以不依赖。提供基于代码模式字节码增强实现的方式。 Sisyphus 支持过程式编程和注解编程的 java 重试框架。 特性 支持 fluent 过程式编程 基于字节码的代理重试 基于注解的重试,允许自定义注解 无缝接入 sp...

    宋华 评论0 收藏0
  • 后端经验

    摘要:在结构上引入了头结点和尾节点,他们分别指向队列的头和尾,尝试获取锁入队服务教程在它提出十多年后的今天,已经成为最重要的应用技术之一。随着编程经验的日积月累,越来越感觉到了解虚拟机相关要领的重要性。 JVM 源码分析之 Jstat 工具原理完全解读 http://click.aliyun.com/m/8315/ JVM 源码分析之 Jstat 工具原理完全解读 http:...

    i_garfileo 评论0 收藏0
  • Spring入门看这一篇就够了

    摘要:甲乙交易活动不需要双方见面,避免了双方的互不信任造成交易失败的问题。这就是的核心思想。统一配置,便于修改。带参数的构造函数创建对象首先,就要提供带参数的构造函数接下来,关键是怎么配置文件了。 前言 前面已经学习了Struts2和Hibernate框架了。接下来学习的是Spring框架...本博文主要是引入Spring框架... Spring介绍 Spring诞生: 创建Spring的...

    superw 评论0 收藏0

发表评论

0条评论

includecmath

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<