LoginSignup
1
8

More than 3 years have passed since last update.

JavaでオブジェクトをCSV形式かつ複数行ヘッダー&フィルターして返したい

Last updated at Posted at 2019-10-27

1.はじめに

csv形式で複数行ヘッダーかつフィールド名で出力フィールドをフィルターできるものを作る必要があったため、使用したライブラリと考えたプログラムを記載する。

2.CsvMapper/CsvSchema

Jacksonのライブラリ(jackson-dataformat-csv)でオブジェクト(json)をcsvに簡単に変換できる。
今回はこれを使用した。

// getter/setterは必須だが省略
class TestObj {
  private String field1 = "aaa";
  private String field2 = null;
  private String field3 = "ccc";
}
TestObj obj = new TestObj();
CsvMapper mapper = new CsvMapper();
CsvSchema schema = mapper.schemaFor(TestObj.class).withHeader();

try {
  System.out.println(mapper.writer(schema).writeValueAsString(obj));
} catch(JsonProcessingException e) {
  // エラー処理(仮)
  e.printStackTrace();
}

[出力]

field1,field2,field3
aaa,,ccc

他にも以下のようなことができる

  • nullのフィールドを指定文字列で補完
CsvMapper mapper = new CsvMapper();
CsvSchema schema = mapper.schemaFor(TestObj.class).withHeader().withNullValue("<empty>");

[出力]

field1,field2,field3
aaa,<empty>,ccc
  • 行の境界とする文字列を指定
CsvMapper mapper = new CsvMapper();
CsvSchema schema = mapper.schemaFor(TestObj.class).withHeader().withLineSeparator("|");

[出力]

"field1","field2","field3"|"aaa",,"ccc"|
  • Listで複数行出力
List<TestObj> objs = new ArrayList<TestObj>() {
  {
    add(new TestObj());
    add(new TestObj());
    add(new TestObj());
  }
};
CsvMapper mapper = new CsvMapper();
CsvSchema schema = mapper.schemaFor(TestObj.class).withHeader();

[出力]

field1,field2,field3
aaa,,ccc
aaa,,ccc
aaa,,ccc

注意が必要なのは、以下のようなネストのオブジェクトの変更には対応していない点である。

// csvへの変換可
class TestObj {
  private String field1 = "aaa";
  private String field2 = "bbb";
  private String field3 = "ccc";
}

// csvへの変換不可
class TestObj2 {
  TestObj obj = new TestObj();
  private String field4 = "aaa";
}

3. 複数行ヘッダー&フィールドでフィルターして出力

オブジェクトのフィールド名と型名をヘッダー(2行)として出力するものを作成する。

残念ながら、jacksonでは複数ヘッダーやフィルターはサポートされていないため、自前で作成した。

※調べが足りないだけかもしれないが、これができるライブラリを見つけられなかった


public static void main(String[] args) {
    TestObj obj = new TestObj();
    List<Field> fields = Arrays.asList(obj.getClass().getDeclaredFields());
    CsvMapper mapper = new CsvMapper();
    CsvSchema schema = mapper.schemaFor(TestObj.class);

    // フィルターするフィールド(本来は外部入力)
    List<String> filterParam = new ArrayList<String>() {
        {
            add("field1");
            add("field3");
        }
    };
    // 複数ヘッダー作成&フィルター
    String header = createHeader(obj, filterParam, fields);
    List<String> bodies = new ArrayList<>();
    try {
        bodies = Arrays.asList(mapper.writer(schema).writeValueAsString(obj).split(","));
    } catch (JsonProcessingException e) {
        // エラー処理(仮)
        e.printStackTrace();
    }
    String body = filter(bodies, filterParam, fields);

    // 本来は繋げて返す
    System.out.println(header);
    System.out.println(body);
}

private static String createHeader(TestObj obj, List<String> param, List<Field> fields) {
    String fieldHeader = fields.stream().filter(o -> param.contains(o.getName())).map(o -> o.getName
)
            .collect(Collectors.joining(","));
    String typeHeader = fields.stream().filter(o -> param.contains(o.getName())).map(o -> {
        String typeName = o.getType().getName();
        return typeName.substring(typeName.lastIndexOf(".") + 1, typeName.length());
    }).collect(Collectors.joining(","));
    return fieldHeader + "\n" + typeHeader;
}

private static String filter(List<String> bodies, List<String> param, List<Field> fields) {
    if (param == null || param.isEmpty()) {
        return bodies.stream().collect(Collectors.joining(","));
    }
    List<String> res = new ArrayList<>();
    for (int i = 0; i < fields.size(); i++) {
        String fieldName = fields.get(i).getName();
        if (param.contains(fieldName)) {
            res.add(bodies.get(i));
        }
    }
    return res.stream().collect(Collectors.joining(","));
}

[出力結果]

field1,field3
String,String
aaa,ccc

参考サイト

CsvSchema
http://fasterxml.github.io/jackson-dataformat-csv/javadoc/2.8/com/fasterxml/jackson/dataformat/csv/CsvSchema.html#withoutColumns()

Javaでカンマ区切りで文字列連結するいくつかの方法
https://qiita.com/shisama/items/b27d16b3aeb1baf055b1

Java8での文字列連結
https://qiita.com/lonerydeveloper/items/9f7c977c039ad4d24d30

1
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
8