Search code examples
csvrecursiontreehierarchyunivocity

Recursive object write to flat csv file


I need to write object such

class User {
    UUID id;
    String name;
    Role role;
}

class Role {
    String name;
    LocalDate activeFrom;
    Authority owner;
}

class Authority {
    String name;
}

The tricky think is that csv writing should work recursively throught objects tree. Also need to write LocalDate as a String like 2019-11-05 f.e.;

The resulting csv file should be flat and looks like:

user.id,user.name,user.role.name,user.role.activeFrom,user.role.owner.name

// example values


I've tried already OpenCsv:

        StatefulBeanToCsv<User> beanToCsv = new StatefulBeanToCsvBuilder<User>(printWriter).build();
        try {
            beanToCsv.write(users);
        } catch (CsvDataTypeMismatchException | CsvRequiredFieldEmptyException e) {
            e.printStackTrace();
        }
        printWriter.close();

and Univocity parsers:

CsvWriterSettings settings = new CsvWriterSettings();
        ObjectRowWriterProcessor processor = new ObjectRowWriterProcessor();
        settings.setRowWriterProcessor(processor);
        processor.convertType(User.class, );

No luck so far. They can serialize to csv only top level object. Also in OpenCsv I had issue to serialize LocalDate. Can You help me? Advise approach or library?


Solution

  • As I did not get any answer, I will describe my solution. I used library:

    http://super-csv.github.io/super-csv/index.html

    and extension:

    https://super-csv.github.io/super-csv/dozer.html

    Code:

    import org.supercsv.io.dozer.CsvDozerBeanWriter;
    import org.supercsv.prefs.CsvPreference;
    import java.io.IOException;
    import java.io.Writer;
    
    static final String[] FIELD_MAPPING = new String[]{
        "user.id", "user.name", "user.role.name", "user.role.activeFrom", "user.role.owner.name"
    };
    
        try (var beanWriter = new CsvDozerBeanWriter(printWriter, CsvPreference.STANDARD_PREFERENCE)) {
            beanWriter.configureBeanMapping(User.class, FIELD_MAPPING);
            beanWriter.writeHeader(FIELD_MAPPING);
            for (User user : users) {
                beanWriter.write(user);
            }
        }
        printWriter.close();
    

    https://super-csv.github.io/super-csv/xref-test/org/supercsv/example/dozer/Writing.html

    And how You can test it:

    @Test
    void status_written_in_csv_format() {
        // Setup
        WriteCsvToResponse objectUnderTest = new WriteCsvToResponse ();
        StringWriter stringWriter = new StringWriter();
        PrintWriter printWriter = new PrintWriter(stringWriter);
    
        // Given
        Status status = ...
    
        // When
        objectUnderTest.writeStatus(printWriter, status);
    
        // Then
        String actualCsv = stringWriter.toString();
        assertThat(actualCsv.split("\n"))
           .as("Produced CSV")
           .containsExactly(
             "id,storeId,status",
             "42,142,OK");
    }
    

    How to test a method using a PrintWriter?