Octopus
(就是一個名字而已~) 是一個簡單的java excel導入導出工具。目的是不用接觸Apache POI的API就能夠完成簡單的Excel導出導入。 同時,能夠自定義表格樣式,導入檢驗數據合法和轉換數據。php
最大的特色就是導出複雜結構對象時自動繪製表頭java
不BB,直接上圖ios
爲了準確性,演示的例子都是完整的單元測試代碼,均可以在測試路徑找到源代碼,各位客官能夠clone下來跑一下看效果,包括上面這個複雜例子(ApplicantExample)喔git
Github地址github
<dependency>
<groupId>cn.chenhuanming</groupId>
<artifactId>octopus</artifactId>
<version>最新版本請到github查看</version>
</dependency>
複製代碼
咱們從最簡單的例子開始——導出一些地址數據。Address
類只有兩個屬性正則表達式
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address {
private String city;
private String detail;
}
複製代碼
在導出前,咱們須要建立一個XML文件定義怎樣去導出bash
<?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.1.0/octopus.xsd" class="cn.chenhuanming.octopus.entity.Address">
<Field name="city" description="City"/>
<Field name="detail" description="Detail"/>
</Root>
複製代碼
首先,建立Root
根元素。這裏引用octopus.xsd文件幫助咱們編寫XMLmaven
而後,賦值class
屬性,表明咱們要導出的類全限定名ide
最後,建立兩個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");
ConfigFactory configFactory = Octopus.getXMLConfigFactory(is);
try {
Octopus.writeOneSheet(os, configFactory, "address", addresses);
} catch (IOException e) {
System.out.println("export failed");
}
}
}
複製代碼
這是一個完整的單元測試代碼,不過導出Excel其實只要兩步:
ConfigFactory
對象Octopus.writeOneSheet()
,傳入導出的文件,configFactory,工做表的名字和數據下面是導出的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.1.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");
ConfigFactory configFactory = Octopus.getXMLConfigFactory(is);
try {
Octopus.writeOneSheet(os, configFactory, "company", companies);
} catch (IOException e) {
System.out.println("export failed");
}
}
}
複製代碼
最後是導出的Excel文件
Octopus能夠處理更復雜的數據,你能夠在cn.chenhuanming.octopus.example.ApplicantExample
查看這個更復雜的例子
有時你想轉換導出的數據。例如,在上一個例子中,咱們不想導出整個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"/>
複製代碼
最後導出的結果
咱們直接拿上一個例子的導出結果來演示導入,共用同一個ConfigFactory
,直接編寫導入的代碼
//First get the excel file
FileInputStream fis = new FileInputStream(rootPath + "/company2.xlsx");
try {
SheetReader<Company> importData = Octopus.readFirstSheet(fis, configFactory, 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,bad和closed 三個值其中一個,同時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.1.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,能夠看到裏面有非法數據
看一下怎麼編寫Java代碼
@Test
public void importCheckedData() throws IOException, InvalidFormatException {
InputStream is = this.getClass().getClassLoader().getResourceAsStream("wrongCompany.xlsx");
ConfigFactory configFactory = new XmlConfigFactory(this.getClass().getClassLoader().getResourceAsStream("company3.xml"));
final SheetReader<CheckedData<Company>> sheetReader = Octopus.readFirstSheetWithValidation(is,configFactory,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-blankable
和options
,還能夠經過regex
配置正則表達式檢查。當校驗錯誤時,會拋出對應的ParseException
子類
is-blankable
:拋出 CanNotBeBlankException
options
:拋出 NotAllowValueException
regex
:拋出 PatternNotMatchException
你經過這些異常來進行跟進一步的處理。若是上面三種校驗方式不能知足需求,在Formatter
的parse
拋出自定義的ParseException
。Octopus會捕獲它們放到exceptions
列表中,並自動把單元格位置和你的配置內容塞到ParseException
中
以上代碼均可以在測試路徑cn.chenhuanming.octopus.example
找到,經過這些例子能夠感覺下Octopus的魅力
目前只提供XML配置,由於XML和類文件解耦,有時候你沒法修改類代碼時,尤爲是導出場景,XML會是更好的選擇。若是你是"anti-xml",能夠實現註解版ConfigFactory
,把註解配置轉換成Field
,這應該不會很難。後面我有空再弄註解配置吧~
Octopus
類能夠提供一行代碼式的API,讓你不用碰Apache POI的API。可是若是你確實須要用到Apache POI,能夠先看一下Octopus核心類SheetWriter
和SheetReader
代碼。我在設計的時候儘可能考慮擴展,而且徹底基於接口實現,實在不行能夠選擇繼承重寫,屬性基本都是protected,或者直接本身實現接口
提Issue或者email我chenhuanming.cn@gmail.com