不想用POI?几行代码完成Excel导出导入

栏目: 编程语言 · XML · 发布时间: 5年前

内容简介:为了准确性,演示的例子都是完整的单元测试代码,都可以在测试路径找到源代码,各位客官可以clone下来跑一下看效果,包括上面这个复杂例子(ApplicantExample)喔

Octopus (就是一个名字而已~) 是一个简单的java excel导入导出工具。目的是不用接触Apache POI的API就可以完成简单的Excel导出导入。 同时,可以自定义表格样式,导入检验数据合法和转换数据。

最大的特点就是导出复杂结构对象时自动绘制表头

不BB,直接上图

不想用POI?几行代码完成Excel导出导入

为了准确性,演示的例子都是完整的单元测试代码,都可以在测试路径找到源代码,各位客官可以clone下来跑一下看效果,包括上面这个复杂例子(ApplicantExample)喔

Github地址

从Maven导入

为了方便使用,直接用Github做了一个私人仓库

增加仓库

<repositories>
	    <repository>
	        <id>chenhuanming-repo</id>
	        <name>chenhuanming-repo</name>
	        <url>https://raw.githubusercontent.com/zerouwar/my-maven-repo/master</url>
	    </repository>
	</repositories>
复制代码

引入依赖

<dependency>
			<groupId>cn.chenhuanming</groupId>
			<artifactId>octopus</artifactId>
			<version>1.0.0</version>
	</dependency>
复制代码

导出Excel

从最简单的例子开始

我们从最简单的例子开始——导出一些地址数据。 Address 类只有两个属性

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address {
    private String city;
    private String detail;
}
复制代码

在导出前,我们需要创建一个XML文件定义怎样去导出

<?xml version="1.0" encoding="UTF-8"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/zerouwar/my-maven-repo/master/cn/chenhuanming/octopus/1.0.0/octopus.xsd"
      class="cn.chenhuanming.octopus.entity.Address">

    <Field name="city" description="City"/>
    <Field name="detail" description="Detail"/>

</Root>
复制代码

首先,创建 Root 根元素。这里引用 octopus.xsd 文件帮助我们编写XML

然后,赋值 class 属性,代表我们要导出的类全限定名

最后,创建两个 Field 元素,代表要导出类的两个属性

name 属性值就是 Address 里的属性名,实际上Octopus调用其getter方法获取值,所以要确保有getter方法

description 属性会被用在绘制表头

我们可以开始做最后一件事,编写 Java 代码

public class AddressExample {
    List<Address> addresses;

    /**
     * make testing data
     */
    @Before
    public void prepare() {
        addresses = new ArrayList<>();
        DataFactory df = new DataFactory();
        for (int i = 0; i < df.getNumberBetween(5, 10); i++) {
            addresses.add(new Address(df.getCity(), df.getAddress()));
        }
    }

    @Test
    public void export() throws FileNotFoundException {

        //where to export
        String rootPath = this.getClass().getClassLoader().getResource("").getPath();
        FileOutputStream os = new FileOutputStream(rootPath + "/address.xlsx");

        //read config from address.xml
        InputStream is = this.getClass().getClassLoader().getResourceAsStream("address.xml");
        ConfigReader configReader = Octopus.getXMLConfigReader(is);

        try {
            Octopus.writeOneSheet(os, configReader, "address", addresses);
        } catch (IOException e) {
            System.out.println("export failed");
        }
    }
}
复制代码

这是一个完整的单元测试代码,不过导出Excel其实只要两步:

ConfigReader
Octopus.writeOneSheet()

下面是导出的Excel文件

不想用POI?几行代码完成Excel导出导入

自动绘制表头

Octopus支持导出复杂对象时自动绘制表头

这次我们来导出一些公司数据,这里是 Company

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Company {
    private String name;
    private Address address;
}
复制代码

然后我们创建一个 company.xml 配置文件

<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/zerouwar/my-maven-repo/master/cn/chenhuanming/octopus/1.0.0/octopus.xsd"
      class="cn.chenhuanming.octopus.entity.Address">


    <Field name="name"
           description="Name"
           color="#ff0000"/>

    <Header name="address" description="Address">
        <Field name="city" description="City"/>
        <Field name="detail" description="Detail"/>
    </Header>

</Root>
复制代码

我们用 Header 元素代表要导出 Company 的一个复杂属性,同时设置字体颜色是红色

Java代码基本跟之前的一样

public class CompanyExample {
    List<Company> companies;

    /**
     * make testing data
     */
    @Before
    public void prepare() {
        companies = new ArrayList<>();
        DataFactory df = new DataFactory();
        for (int i = 0; i < df.getNumberBetween(5, 10); i++) {
            companies.add(new Company(df.getBusinessName(), new Address(df.getCity(), df.getAddress())));
        }
    }

    @Test
    public void export() throws FileNotFoundException {

        //where to export
        String rootPath = this.getClass().getClassLoader().getResource("").getPath();
        FileOutputStream os = new FileOutputStream(rootPath + "/company.xlsx");

        //read config from company.xml
        InputStream is = this.getClass().getClassLoader().getResourceAsStream("company.xml");
        ConfigReader configReader = Octopus.getXMLConfigReader(is);

        try {
            Octopus.writeOneSheet(os, configReader, "company", companies);
        } catch (IOException e) {
            System.out.println("export failed");
        }
    }

}
复制代码

最后是导出的Excel文件

不想用POI?几行代码完成Excel导出导入

Octopus可以处理更复杂的数据,你可以在 cn.chenhuanming.octopus.example.ApplicantExample 查看这个更复杂的例子

不想用POI?几行代码完成Excel导出导入

转换数据

有时你想转换导出的数据。例如,在上一个例子中,我们不想导出整个 Address 对象,把它当做一个字符串导出

我们所需要做的只是实现一个 Formatter

public class AddressFormatter implements Formatter<Address> {
    @Override
    public String format(Address address) {
        return address.getCity() + "," + address.getDetail();
    }

    @Override
    public Address parse(String str) {
        String[] split = str.split(",");
        if (split.length != 2) {
            return null;
        }
        return new Address(split[0], split[1]);
    }
}
复制代码

parse 方法用于导入Excel时,只要关注 format 方法。这里接受一个Address对象,返回一个字符串。

最后,配置 AddressFormatter 到XML文件

<Field name="name"
          description="Name"
          color="#ff0000"/>

<Field name="address"
      description="Address"
      formatter="cn.chenhuanming.octopus.formatter.AddressFormatter"/>
复制代码

最后导出的结果

不想用POI?几行代码完成Excel导出导入

导入Excel

我们直接拿上一个例子的导出结果来演示导入,共用同一个 ConfigReader ,直接编写导入的代码

//First get the excel file
FileInputStream fis = new FileInputStream(rootPath + "/company2.xlsx");

try {
    SheetReader<Company> importData = Octopus.readFirstSheet(fis, configReader, new DefaultCellPosition(1, 0));

    for (Company company : importData) {
        System.out.println(company);
    }
} catch (Exception e) {
    System.out.println("import failed");
}
复制代码

在控制台可以看到打印导入结果,可以看到,之前的 AddressFormatter 也完成了数据的转换工作

Company(name=Graham Motor Services, address=Address(city=Monroe, detail=666 Bonnair Ave))
Company(name=Social Circle Engineering, address=Address(city=Fort Gaines, detail=956 Third Ridge))
Company(name=Enigma Cafe, address=Address(city=Mcdonough, detail=1278 Midway Trail))
Company(name=Hapeville Studios, address=Address(city=Riceboro, detail=823 Tuscarawas Blvd))
Company(name=Thalman Gymnasium, address=Address(city=Ebenezer, detail=1225 Blackwood Avenue))
Company(name=Sparks Pro Services, address=Address(city=Darien, detail=1362 Woodlawn Lane))
Company(name=Toccoa Development, address=Address(city=Ridgeville, detail=1790 Lawn Ave))
复制代码

导入校验数据

有时候我们对导入的数据有一定的要求,Octopus提供简单的数据校验配置

首先给我们的 Company 增加一个 status 属性,只能是 good , badclosed 三个值其中一个,同时 name 不可以为空,看一下XML配置文件

<?xml version="1.0" encoding="UTF-8"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/zerouwar/my-maven-repo/master/cn/chenhuanming/octopus/1.0.0/octopus.xsd"
      class="cn.chenhuanming.octopus.entity.Company">


    <Field name="name"
           description="Name"
           color="#ff0000"
           is-blankable="false"/>

    <Field name="address"
           description="Address"
           formatter="cn.chenhuanming.octopus.formatter.AddressFormatter"
    />

    <Field name="status"
           description="Status"
           options="good|bad|closed"/>
    <!--| split options -->
    
</Root>
复制代码

这是我们要导入的Excel,可以看到里面有非法数据

不想用POI?几行代码完成Excel导出导入

看一下怎么编写Java代码

@Test
public void importCheckedData() throws IOException, InvalidFormatException {
    InputStream is = this.getClass().getClassLoader().getResourceAsStream("wrongCompany.xlsx");

    ConfigReader configReader = new XmlConfigReader(this.getClass().getClassLoader().getResourceAsStream("company3.xml"));

    final SheetReader<CheckedData<Company>> sheetReader = Octopus.readFirstSheetWithValidation(is,configReader,new DefaultCellPosition(1,0));

    for (CheckedData<Company> checkedData : sheetReader) {
        System.out.println(checkedData);
    }
}
复制代码

这里我们调用 Octopus.readFirstSheetWithValidation ,获取带校验结果的 SheetReader ,看一下导入的结果

CheckedData(data=Company(name=Graham Motor Services, address=Address(city=Monroe, detail=666 Bonnair Ave), status=good), exceptions=[])
CheckedData(data=Company(name=Social Circle Engineering, address=Address(city=Fort Gaines, detail=956 Third Ridge), status=null), exceptions=[cn.chenhuanming.octopus.exception.NotAllowValueException])
CheckedData(data=Company(name=null, address=Address(city=Mcdonough, detail=1278 Midway Trail), status=null), exceptions=[cn.chenhuanming.octopus.exception.CanNotBeBlankException, cn.chenhuanming.octopus.exception.NotAllowValueException])
复制代码

可以看到每一个 CheckData 有一个 data 属性和一个 exceptions 列表。 这个异常列表存放着导入时每一个单元格可能出现的校验错误,异常类型都是 ParseException

除了 is-blankableoptions ,还可以通过 regex 配置正则表达式检查。当校验错误时,会抛出对应的 ParseException 子类

  • is-blankable :抛出 CanNotBeBlankException
  • options :抛出 NotAllowValueException
  • regex :抛出 PatternNotMatchException

你可以通过这些异常来进行跟进一步的处理。如果上面三种校验方式不能满足需求,在 Formatterparse 抛出自定义的 ParseException 。Octopus会捕获它们放到 exceptions 列表中,并自动把单元格位置和你的配置内容塞到 ParseException 中,这样你可以自己选择处理这些异常数据

以上代码都可以在测试路径 cn.chenhuanming.octopus.example 找到,通过这些例子可以感受下Octopus的魅力

Q&A

没有Java注解配置?

目前只提供XML配置,因为XML和类文件解耦,有时候你无法修改类代码时,尤其是导出场景,XML会是更好的选择。如果你是"anti-xml",可以实现注解版 ConfigReader ,把注解配置转换成 Field ,这应该不会很难。后面我有空再弄注解配置吧~


以上所述就是小编给大家介绍的《不想用POI?几行代码完成Excel导出导入》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

用户故事地图

用户故事地图

Jeff Patton / 李涛、向振东 / 清华大学出版社 / 2016-4-1 / 59.00元

用户故事地图作为一种有效的需求工具,越来越广泛地应用于开发实践中。本书以用户故事地图为主题,强调以合作沟通的方式来全面理解用户需求,涉及的主题包括怎么以故事地图的方式来讲用户需求,如何分解和优化需求,如果通过团队协同工作的方式来积极吸取经验教训,从中洞察用户的需求,开发真正有价值的、小而美的产品和服务。本书适合产品经理、用户体验设计师、产品负责人、业务分析师、IT项目经理、敏捷教练和精益教练阅读和......一起来看看 《用户故事地图》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具