カテゴリー
コンピューター

【Groovy】OpenCSV を使えるかどうか試しました。使用例とわかった問題点

最初に。参考にしたページ

OpenCSV を使用した POJO インポート

エスケープ文字について

  • 半角ダブルクオーテーション ” をクオート文字、およびエスケープ文字に指定するとエラー (java.lang.UnsupportedOperationException) となってしまう。
  • では、エスケープ文字として半角ダブルクオーテーションを使用できないかと思うが、そんなことはなかった。
  • 半角バックスラッシュ(半角エンマーク) ¥ をエスケープ文字として指定してみたが、半角ダブルクオーテーションもエスケープ文字として使用できた。
  • 同じく疑問、というか、ハマった人もいたみたいです。
opencsv / Feature Requests / #72 Allow double quote as escape character AND as quote character

エラーとなった場合の出力

Caught: java.lang.UnsupportedOperationException: The separator, quote, and escape characters must be different!
java.lang.UnsupportedOperationException: The separator, quote, and escape characters must be different!
    at com.opencsv.CSVParser.<init>(CSVParser.java:211)
    at com.opencsv.CSVParserBuilder.build(CSVParserBuilder.java:138)
    at com.opencsv.bean.CsvToBeanBuilder.buildParser(CsvToBeanBuilder.java:193)
    at com.opencsv.bean.CsvToBeanBuilder.build(CsvToBeanBuilder.java:145)
    at com.opencsv.bean.CsvToBeanBuilder$build$0.call(Unknown Source)
    at ImportPojo.run(ImportPojo.groovy:11)

POJO インポート例

実行コードです。

// https://mvnrepository.com/artifact/com.opencsv/opencsv
@Grapes(
    @Grab(group='com.opencsv', module='opencsv', version='4.1')
)

import com.opencsv.CSVReader
import com.opencsv.bean.CsvToBeanBuilder

println "入力"
List<Customer> customers = new CsvToBeanBuilder(new FileReader("input.csv"))
    .withSeparator(',' as char)
    .withQuoteChar('"' as char)
    // \ をエスケープ文字に指定したが、" もエスケープ文字になっている
    .withEscapeChar('\\' as char)
    .withType(Customer.class).build().parse()

customers.eachWithIndex { record,i ->
    println "${i}行目----------"
    println "name[${record.name}]"
    println "age[${record.age}]"
}

POJO クラスです。

import com.opencsv.bean.CsvBindByName

public class Customer {
    @CsvBindByName(column = "氏名")
    public String name

    @CsvBindByName(column = "年齢")
    public String age
}

CSV ファイルです。

"氏名","年齢"
"岩田\"一恵",41
"東""宏行",36
梅沢 照生,38
"児島,
りえ",65

実行結果です。

$ groovy ImportPojo.groovy 
入力
0行目----------
name[岩田"一恵]
age[41]
1行目----------
name[東"宏行]
age[36]
2行目----------
name[梅沢 照生]
age[38]
3行目----------
name[児島,
りえ]
age[65]
$

OpenCSV を使用した POJO インポート

まとめメモ

  • POJO クラスのプラパティは、public アクセス修飾子を付けてはいけない。エラーとなる。
  • クラス宣言にアクセス修飾子を付けるのは問題ない。
  • デフォルトのエスケープ文字は半角ダブルクオーテーション ” だった。
  • デフォルトで、区切り文字が付与され、それは半角ダブルクオーテーション ” だった。
  • 出力される CSV のカラムの順番は、POJO クラスのプロパティをソートした昇順となる(少なくとも opencsv 4.1 では)。

POJO クラスのプロパティに public アクセス修飾子を付けた結果

インポートで使用した Customer.groovy そそのまま使用すると、次のエラーとなりました><。

$ groovy ExportPojo.groovy 
出力
Exception in thread "pool-1-thread-1" Exception in thread "pool-1-thread-2" com.opencsv.exceptions.CsvBeanIntrospectionException: An introspection error was thrown while attempting to manipulate property age of bean Customer.
	at com.opencsv.bean.AbstractBeanField.write(AbstractBeanField.java:248)
	at com.opencsv.bean.concurrent.ProcessCsvBean.writeWithReflection(ProcessCsvBean.java:80)
	at com.opencsv.bean.concurrent.ProcessCsvBean.run(ProcessCsvBean.java:107)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NoSuchMethodException: Unknown property 'age' on class 'class Customer'
	at org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(PropertyUtilsBean.java:1269)
	at com.opencsv.bean.AbstractBeanField.write(AbstractBeanField.java:245)
	... 5 more
com.opencsv.exceptions.CsvBeanIntrospectionException: An introspection error was thrown while attempting to manipulate property age of bean Customer.
	at com.opencsv.bean.AbstractBeanField.write(AbstractBeanField.java:248)
	at com.opencsv.bean.concurrent.ProcessCsvBean.writeWithReflection(ProcessCsvBean.java:80)
	at com.opencsv.bean.concurrent.ProcessCsvBean.run(ProcessCsvBean.java:107)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NoSuchMethodException: Unknown property 'age' on class 'class Customer'
	at org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(PropertyUtilsBean.java:1269)
	at com.opencsv.bean.AbstractBeanField.write(AbstractBeanField.java:245)
	... 5 more
Caught: java.lang.RuntimeException: There was an unrecoverable error while writing beans.
java.lang.RuntimeException: There was an unrecoverable error while writing beans.
	at com.opencsv.bean.StatefulBeanToCsv.write(StatefulBeanToCsv.java:329)
	at com.opencsv.bean.StatefulBeanToCsv$write.call(Unknown Source)
	at ExportPojo.run(ExportPojo.groovy:26)
Caused by: java.lang.NoSuchMethodException: Unknown property 'age' on class 'class Customer'
	at org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(PropertyUtilsBean.java:1269)
	at com.opencsv.bean.AbstractBeanField.write(AbstractBeanField.java:245)
	at com.opencsv.bean.concurrent.ProcessCsvBean.writeWithReflection(ProcessCsvBean.java:80)
	at com.opencsv.bean.concurrent.ProcessCsvBean.run(ProcessCsvBean.java:107)
$

POJO エクスポート例

実行ファイルです。

// https://mvnrepository.com/artifact/com.opencsv/opencsv
@Grapes(
    @Grab(group='com.opencsv', module='opencsv', version='4.1')
)

import com.opencsv.CSVWriter
import com.opencsv.bean.StatefulBeanToCsv
import com.opencsv.bean.StatefulBeanToCsvBuilder

List<Customer> customers = [
    new Customer(name: "試験太郎", age: "14"),
    new Customer(name: "試験\"\"花子", age: "15")
]

println "出力"
Writer writer = new FileWriter("output.csv")
StatefulBeanToCsv beanToCsv = new StatefulBeanToCsvBuilder(writer)
    .build()
beanToCsv.write(customers)
writer.close()
println "output.csv を確認して!"

POJO です。インポートのときと異なり、プロパティからアクセス修飾子を除いています。

import com.opencsv.bean.CsvBindByName

public class Customer {
    @CsvBindByName(column = "氏名")
    String name

    @CsvBindByName(column = "年齢")
    String age
}

実行した結果、出力されたファイルの内容です。

"年齢","氏名"
"14","試験太郎"
"15","試験""""花子"

おわりに

今回試したコードは、次のリポジトリのコミットにまとめました。

現在判明している、深刻な問題はとして上述しましたが次があります。

  • エクスポート時、出力される CSV のカラムの順番は、POJO クラスのプロパティをソートした昇順となる(少なくとも opencsv 4.1 では)。
  • よって出力される CSV カラム順番を制御するには、とりあえず、簡単に解決するには、POJO クラスのプロパティ名に接頭辞等を付けて順番を付ければ良いと思う。

とりあえずの解決策を記しましたが、あまり良くないですね><。

それはともかく、opencsv を Groovy から使えることは少なくともわかりましたので、嬉しく思います♪

以上です。

コメントを残す