Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

SYM_simu
HN:SYM(シム) Javaer 地獄を生き延びた代償=エンジニア生命損傷 現状を打破すべく独学再スタート(2020/4~) 勉強中: TS/JS/Vue/アーキテクチャ/Python/RoR
https://github.com/Symthy
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away