初めに
本記事は、JavaのOpenCSVの使用方法について記載しています。Gradleを使用したJavaの開発環境があれば問題ないと思います。わからない方は以下の参考にしてください。
OpenCSVを導入
以下からライブラリを追加してください。
以下のようにbuild.gradleを記述してください。
plugins {
id 'application'
// 外部jarを読み込めるようにする
id 'com.github.johnrengelman.shadow' version '7.1.2'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'junit:junit:4.13.2'
implementation 'com.google.guava:guava:30.1.1-jre'
// 追加 opencsv
implementation 'com.opencsv:opencsv:5.6'
// 追加 lombok
implementation 'org.projectlombok:lombok:1.18.24'
}
application {
mainClass = 'app.App'
}
// jar追加
jar {
manifest {
attributes 'Main-Class': 'app.App'
}
}
OpenCSVの使用方法
OpenCSVの使用方法について記載します。本記事では、単純なCSVファイルの出力方法とCSVファイルの読み込み方法、JavaBeansを使用したCSVファイルの出力方法とCSVファイルの読み込みについて記述します。
単純なCSVファイルの出力方法
List形式のデータをCSV出力する方法です。配列が1行のデータ(カンマ区切りの数)を指し、リストが行数を指します。
package app.pattern1.util;
import java.io.File;
import java.io.FileWriter;
import java.util.List;
import com.opencsv.CSVWriter;
public class Pattern1WriteCSVUtil {
/**
* CSVファイルを出力する
* @param path CSVファイルのパス
* @param dataList 書き込むデータリスト
* @throws Exception
*/
public static void writeCSV(String path, List<String[]> dataList) throws Exception{
File file = new File(path);
try (CSVWriter csvWriter = new CSVWriter(new FileWriter(file))) {
csvWriter.writeAll(dataList);
} catch (Exception e) {
e.printStackTrace();
throw new Exception();
}
}
}
単純なCSVファイルの読み込み方法
CSVデータをList形式で読み込む方法です。配列が1行のデータ(カンマ区切りの数)を指し、リストが行数を指します。
package app.pattern1.util;
import java.io.File;
import java.io.FileReader;
import java.util.List;
import com.opencsv.CSVReader;
public class Pattern1ReadCSVUtil {
/**
* CSVファイルを読み込む
* @param path CSVファイルのパス
* @return 読み込んだCSVデータ
* @throws Exception
*/
public static List<String[]> readCSV(String path) throws Exception{
File file = new File(path);
try (CSVReader csvReader = new CSVReader(new FileReader(file))) {
return csvReader.readAll();
} catch (Exception e) {
e.printStackTrace();
throw new Exception();
}
}
}
JavaBeansを使用したCSVファイルの出力方法
JavaBeansを使用したCSVファイルの出力方法です。例えば、「ID、名前、年齢、所属」の情報をもつ「 UserInfoBean.java 」というJavaBeansのクラスがあるとします。この時、 単純なCSVファイルの読み込み方法の方法だと毎回、データをString型に変換しないといけないので面倒ですね。「 UserInfoBean.java 」のまま出力させられたら便利ですね。今回はその方法についてです。
JavaBeansの修正
以下のような「 UserInfoBean.java 」を想定します。
package app.pattern2.bean;
import com.opencsv.bean.CsvBindByName;
import com.opencsv.bean.CsvBindByPosition;
import org.apache.commons.lang3.builder.ToStringBuilder;
import lombok.Data;
@Data
public class UserInfoBean {
private int id;
private String name;
private int age;
private String belong;
public UserInfoBean(int id, String name, int age, String belong){
this.id = id;
this.name = name;
this.age = age;
this.belong = belong;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
これを以下のように変更してください。
package app.pattern2.bean;
import com.opencsv.bean.CsvBindByName;
import com.opencsv.bean.CsvBindByPosition;
import org.apache.commons.lang3.builder.ToStringBuilder;
import lombok.Data;
@Data
public class UserInfoBean {
@CsvBindByPosition(position = 0)
@CsvBindByName(column = "ID", required = true)
private int id;
@CsvBindByPosition(position = 1)
@CsvBindByName(column = "名前", required = true)
private String name;
@CsvBindByPosition(position = 2)
@CsvBindByName(column = "年齢", required = true)
private int age;
@CsvBindByPosition(position = 3)
@CsvBindByName(column = "所属", required = true)
private String belong;
public UserInfoBean(int id, String name, int age, String belong){
this.id = id;
this.name = name;
this.age = age;
this.belong = belong;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
2つの@(アノテーション)を追加しました。それぞれ、「@CsvBindByPosition」はCSVファイルを出力する際のヘッダーの順番を指定します。次に、「@CsvBindByName」はヘッダーの名前と必須チェックの有無を指定しています。
JavaBeans形式でCSV出力する方法
以下の公式サイトを以下のように実装しました。
@Test
public void test() throws Exception{
// csv読み込み(Bean)
String path = "./csv/result.csv";
UserInfoBean beans = new UserInfoBean(1,"Tanaka",20,"チームB");
Writer writer = new FileWriter(path);
StatefulBeanToCsv beanToCsv = new StatefulBeanToCsvBuilder(writer).build();
beanToCsv.write(beans);
writer.close();
}
すると以下のような結果になってしいます。ヘッダー名が設定されていませんね。
上記の実装だと2つのアノテーションのうちどちらか片方しか、機能しません。
"1","Tanaka","20","チームB"
2つのアノテーションの両方が機能するようにします。
まずは、「ColumnPositionMappingStrategy」を継承した「CustomMappingStrategy」を作成します。パッケージ以外はコピペでOKです。よくわからないかと思いますが、「extractHeaderName」や「columnHeaderName」とあるのでなんとなくJavaBeansに設定した「@CsvBindByName」から良い感じにヘッダーの名前を取得しようとしていると思ってください。
package app.pattern2.util;
import com.opencsv.bean.BeanField;
import com.opencsv.bean.ColumnPositionMappingStrategy;
import com.opencsv.bean.CsvBindByName;
import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
import org.apache.commons.lang3.StringUtils;
public class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T>{
@Override
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
final int numColumns = getFieldMap().values().size();
super.generateHeader(bean);
String[] header = new String[numColumns];
BeanField beanField;
for (int i = 0; i < numColumns; i++) {
beanField = findField(i);
String columnHeaderName = extractHeaderName(beanField);
header[i] = columnHeaderName;
}
return header;
}
private String extractHeaderName(final BeanField beanField) {
if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(
CsvBindByName.class).length == 0) {
return StringUtils.EMPTY;
}
final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0];
return bindByNameAnnotation.column();
}
}
任意のJavaBeansを読み込めるCSV出力機能の実装
package app.pattern2.util;
import java.io.File;
import java.io.FileWriter;
import java.util.List;
import com.opencsv.CSVWriter;
import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;
public class Pattern2WriteCSVUtil<T>{
/**
* CSVファイルを出力する
* @param beanList 書き込むデータリスト
* @param bean JavaBeansのクラス
* @param path CSVファイルのパス
* @throws Exception
*/
public void writeCSV(List<T> beanList, Class<T> bean, String path) throws Exception {
File file = new File(path);
try (CSVWriter csvWriter = new CSVWriter(new FileWriter(file))) {
CustomMappingStrategy<T> mappingStrategy = new CustomMappingStrategy<>();
mappingStrategy.setType(bean);
StatefulBeanToCsv<T> beanToCsv = new StatefulBeanToCsvBuilder<T>(csvWriter)
.withSeparator(CSVWriter.DEFAULT_SEPARATOR)
.withMappingStrategy(mappingStrategy)
.build();
beanToCsv.write(beanList);
} catch (Exception e) {
e.printStackTrace();
throw new Exception();
}
}
}
使い方は以下のように行います。
Pattern2WriteCSVUtil<使用したいJavaBeans> 任意の変数名 = new Pattern2WriteCSVUtil<使用したいJavaBeans>();
任意の変数名.writeCSV();
実際に以下のように呼び出してみます。
@Test
public void test() throws Exception{
String path = "./csv/result.csv";
List<UserInfoBean> beanList = new ArrayList<>();
UserInfoBean bean = new UserInfoBean(1,"Tanaka",20,"チームB");
beanList.add(bean);
Pattern2WriteCSVUtil<UserInfoBean> util = new Pattern2WriteCSVUtil<UserInfoBean>();
util.writeCSV(beanList, UserInfoBean.class, path);
}
上記の出力結果です。header名も設定されていますね。
"ID","名前","年齢","所属"
"1","Tanaka","20","チームB"
CSVの出力はこれで終わりです。
JavaBeans形式でCSV読み込みする方法
CSV読み込みは書き込みほど面倒ではありません。ほぼ公式サイトと同じ実装です。
package app.pattern2.util;
import java.io.File;
import java.io.FileReader;
import java.util.List;
import com.opencsv.CSVReader;
import com.opencsv.bean.CsvToBeanBuilder;
public class Pattern2ReadCSVUtil<T> {
public List<T> readCSV(Class<T> bean, String path) throws Exception{
File file = new File(path);
try (CSVReader csvReader = new CSVReader(new FileReader(file))) {
CsvToBeanBuilder<T> builder = new CsvToBeanBuilder<T>(csvReader);
builder.withType(bean);
return builder.build().parse();
} catch (Exception e) {
e.printStackTrace();
throw new Exception();
}
}
}
ここで注意して欲しいことがあります。「UserInfoBean」は id や age が int型 になっています。しかし、CSVファイルから読み込む際はString型の扱いになるので「UserInfoBean」で読み込むとエラーになります。
よって以下のように新たに「UserInfoBean2」を作成します。
package app.pattern2.bean;
import com.opencsv.bean.CsvBindByName;
import com.opencsv.bean.CsvBindByPosition;
import org.apache.commons.lang3.builder.ToStringBuilder;
import lombok.Data;
@Data
public class UserInfoBean2 {
@CsvBindByPosition(position = 0)
@CsvBindByName(column = "ID", required = true)
private String id;
@CsvBindByPosition(position = 1)
@CsvBindByName(column = "名前", required = true)
private String name;
@CsvBindByPosition(position = 2)
@CsvBindByName(column = "年齢", required = true)
private String age;
@CsvBindByPosition(position = 3)
@CsvBindByName(column = "所属", required = true)
private String belong;
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
先ほど出力したCSVを読み込んでみましょう。
@Test
public void test6() throws Exception{
String path = "./csv/result.csv";
Pattern2ReadCSVUtil<UserInfoBean2> csvUtil = new Pattern2ReadCSVUtil<UserInfoBean2>();
List<UserInfoBean2> dataList = csvUtil.readCSV(UserInfoBean2.class, path);
for(UserInfoBean2 info : dataList){
System.out.println(info);
}
}
上記の実行結果です。ちゃんと変数と値が紐づいてますね。
app.pattern2.bean.UserInfoBean2@6d7b4f4c[age=年齢,belong=所属,id=ID,name=名前]
app.pattern2.bean.UserInfoBean2@eec5a4a[age=20,belong=チームB,id=1,name=Tanaka]
最後に
ここまでご覧いただきありがとうございます。以下は投稿者が作成した今回のOpenCSVの記事で使用した4パターンの読み書きの機能を実装したJavaプロジェクトです。記事では省いている部分もありますのでご参考までに・・・。
参考