Simple Spreadsheet Processing for Java

An apache-poi wrapper which lets you read or write an spreadsheet file with a simple java statement

License

License

GroupId

GroupId

com.github.chenjianjx
ArtifactId

ArtifactId

sep4j
Last Version

Last Version

2.0.5
Release Date

Release Date

Type

Type

jar
Description

Description

Simple Spreadsheet Processing for Java
An apache-poi wrapper which lets you read or write an spreadsheet file with a simple java statement
Project URL

Project URL

https://github.com/chenjianjx/sep4j
Source Code Management

Source Code Management

https://github.com/chenjianjx/sep4j

Download sep4j

How to add to project

<!-- https://jarcasting.com/artifacts/com.github.chenjianjx/sep4j/ -->
<dependency>
    <groupId>com.github.chenjianjx</groupId>
    <artifactId>sep4j</artifactId>
    <version>2.0.5</version>
</dependency>
// https://jarcasting.com/artifacts/com.github.chenjianjx/sep4j/
implementation 'com.github.chenjianjx:sep4j:2.0.5'
// https://jarcasting.com/artifacts/com.github.chenjianjx/sep4j/
implementation ("com.github.chenjianjx:sep4j:2.0.5")
'com.github.chenjianjx:sep4j:jar:2.0.5'
<dependency org="com.github.chenjianjx" name="sep4j" rev="2.0.5">
  <artifact name="sep4j" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.github.chenjianjx', module='sep4j', version='2.0.5')
)
libraryDependencies += "com.github.chenjianjx" % "sep4j" % "2.0.5"
[com.github.chenjianjx/sep4j "2.0.5"]

Dependencies

compile (4)

Group / Artifact Type Version
commons-io : commons-io jar 2.4
commons-lang : commons-lang jar 2.6
commons-beanutils : commons-beanutils-core jar 1.8.3
org.apache.poi : poi-ooxml jar 3.8

test (2)

Group / Artifact Type Version
junit : junit jar 4.9
com.google.guava : guava jar 21.0

Project Modules

There are no modules declared in this project.

#f03c15 Please use ssio instead, annotation-based and strong-typed in cells


Sep4j = Simple Spreadsheet Processing for Java


It's a wrapper of Apache POI, with which you can do javabeans <-> spreadsheet conversion even more easily.

Quick Start

pom.xml

<dependencies>
	<dependency>
		<groupId>com.github.chenjianjx</groupId>
		<artifactId>sep4j</artifactId>
		<version>2.0.5</version>
	</dependency>
	..		
</dependencies>	

Save(Write)

		
Map<String, String> headerMap = new LinkedHashMap<String, String>();
headerMap.put("userId", "User Id"); // "userId" is a property of the javabeans you are going to save.
			     // "User Id" will be the corresponding column header in the spreadsheet.
headerMap.put("firstName", "First Name");
headerMap.put("lastName", "Last Name");

OutputStream spreadsheetOutputStream = new FileOutputStream("someExcelFile.xlsx");
Ssio.save(headerMap, userList, spreadsheetOutputStream);	
//"spreadsheetOutputStream" can be replaced with "spreadsheetOutputFile" (a java.io.File object) 	

or if you use Guava, you can just

Ssio.save(
    ImmutableMap.of("userId", "User Id", "firstName","First Name", "lastName", "Last Name"), 
    userList, spreadsheetOutputStream);

You can even let the program generate a header map for you:

Ssio.save(User.class, userList, spreadsheetOutputStream);

You will get an spreadsheet file like

User Id First Name Last Name
1 Lei Li
2 Jim Green

Note: All cells generated will be String-Typed Cells.

Parse(Read)

Map<String, String> reverseHeaderMap = new HashMap<String,String>();
reverseHeaderMap.put("User Id", "userId");  //"User Id" is a column header in the spreadsheet.
					//"userId" is the corresponding property of User class.
reverseHeaderMap.put("First Name", "firstName");
reverseHeaderMap.put("Last Name","lastName");

InputStream spreadsheetInputStream = new FileInputStream("someExcelFile.xlsx");
List<User> users = Ssio.parseIgnoringErrors(reverseHeaderMap, spreadsheetInputStream, User.class); 
//"spreadsheetInputStream" can be replaced with "spreadsheetInputFile" (a java.io.File object) 	

or if you use Guava, you can just

List<User> users = Ssio.parse(
    ImmutableMap.of("User Id","userId","First Name","firstName","Last Name","lastName"),
    spreadsheetInputStream,  User.class);

You can even let the program guess out a reverseHeaderMap for you

List<User> users = Ssio.parseIgnoringErrors(spreadsheetInputStream, User.class);

Error Handling

Save

				
List<DatumError> datumErrors = new ArrayList<DatumError>(); //to collect the errors
headerMap.put("fakeProperty", "Fake Property"); //try to write an non-existing property
Ssio.save(headerMap, users, outputStream, "!!ERROR!!", datumErrors); 		
for (DatumError de : datumErrors) {//here to handle the errors
	System.err.println(MessageFormat.format("Error: recordIndex = {0}, 
	propName = \"{1}\", cause = {2}",
	de.getRecordIndex(), de.getPropName(), de.getCause()));			
}

Will then get an spreadsheet file like

User Id First Name Last Name Fake Property
1 Lei Li !!ERROR!!
2 Jim Green !!ERROR!!

Parse

List<CellError> cellErrors = new ArrayList<CellError>();
try{			
	List<User> users = Ssio.parse(reverseHeaderMap, inputStream, cellErrors, User.class);
}catch (InvalidFormatException e) {
	System.err.println("Not a valid spreadsheet file");
} catch (InvalidHeaderRowException e) {
	System.err.println("The column headers of your spreadsheet file do not match what we need");
}		
for (CellError ce : cellErrors) {
	System.err.println(MessageFormat.format("failed to parse a cell: rowIndexOneBased = {0},
	columnIndexOneBased = {1}, propName = \"{2}\", headerText = \"{3}\", cause = {4} ", 
			ce.getRowIndexOneBased(),ce.getColumnIndexOneBased(),
			ce.getPropName(),ce.getHeaderText(), ce.getCause()));
}

Type Conversions

Save

Sep4j will call the properties' toString() methods to convert a property value to a String, and then write them to an spreadsheet file as String-typed cells.

  • What if I want the property printed another way instead of toString(), for example, to format a date in Chinese style?
    • Create a new, String-typed property in your class by adding a getter method.
private static final class User {
	...	
	public String getBirthDayString(){
		if(birthDay == null){
			return null;
		}			
		return DateFormatUtils.format(birthDay, "yyyy-MM-dd");			
	}
	...	
}

Also, add it to the header map:

headerMap.put("birthDayString", "Birth Date");
  • Can I let Sep4j produce Numeric-typed cells or another type others than String ?
    • No, you can't. This is how Sep4j keeps itself simple.

Parse

  • Sep4j will only take cells of the following types. Cells of other types such as formula, blank etc. will be parsed as null values.

    • String
    • Boolean
    • Numeric
    • Date (Actually it is a Numeric cell type + Date cell style)
  • What if a cell is of String type in the spreadsheet, but its corresponding java property is of double?

    • Sep4j will do a guess for you, if the String's format in the cell is a valid number; You don't need another setter. if the String's format in the cell is not a valid number, Sep4j will report a CellError saying "no suitable setter"
  • A property of my class is not of any basic types. For example, it's of List. What to do?

    • Add a String-Typed setter to your class
public void setRoles(String rolesString){
	String[] roleArray = StringUtils.split(rolesString, ",");
	this.setRoles(Arrays.asList(roleArray));
}
  • Null handling
    • Cell with null value will lead to a null property value. However, if the property is of primitive type such as "int", "long", then a CellError will be raised.

Advanced Usages

Deal with Maps instead of Javabeans

In some cases you have a collection of Maps and you don't want to bother creating a class. This can help:

Save
ImmutableMap<String, Object> record1 = ImmutableMap.of("firstName", "Jim", "lastName", "Green");
ImmutableMap<String, Object> record2 = ImmutableMap.of("firstName", "Li", "lastName", "Lei");
List<Map<String,Object>> records = Arrays.asList(record1, record2);
ImmutableMap<String, String> headerMap = ImmutableMap.of("firstName", "First Name", "lastName", "Last Name");
Ssio.saveMaps(headerMap, records, spreadsheetOutputStream);

You can also let sep4j generate a header map for you, by just providing the map's keys

Ssio.saveMaps(Arrays.asList("firstName", "lastName"), records, spreadsheetOutputStream);

Parse

Map<String, String> reverseHeaderMap = ImmutableMap.of("User Id", "userId",
				"First Name", "firstName", "Last Name", "lastName");
List<Map<String, String>> users = Ssio.parseToMapsIgnoringErrors(reverseHeaderMap,  inputStream);

Append records to a spreadsheet file

Ssio.appendTo(headerMap, newListToAppend, theFile);

Misc

Best Practice for Date-typed properties during parsing

A date column in a spreadsheet may have both String-typed cells and Date-typed cells (common human error). You need to accommodate both.

/**
 * if the cell is of Date type 
 * @param birthDay
 */
public void setBirthDay(Date birthDay) {
	this.birthDay = birthDay;
}

/**
 * if the cell is of String type
 * @param birthDayString
 * @throws ParseException
 */
public void setBirthDay(String birthDayString) throws ParseException {
	if(birthDayString == null){
		return;
	}
	birthDay = DateUtils.parseDate(birthDayString, new String[]{"yyyy-MM-dd"});			
}
		

Versions

Version
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0