Home > Net >  OpenCSV - create row for every entry in list of maps
OpenCSV - create row for every entry in list of maps

Time:06-14

I am trying to implement an DMN (Decision Model and Notation) evaluation service, where the user can upload a csv file with test cases to be evaluated and receive results also as a csv file for every test cases in the input file.

Reading the input csv file and evaluating the test cases works without problems. But I have some issues in writing the results to a csv file using OpenCsv.

Here is the mapped bean, which should be converted to csv row:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class DmnTestCaseResult {

   private Map<String, Object> testInput;

   private Map<String, Object> expectedOutput;

   private List<Map<String, Object>> testOutput;

   private String errorMessage;
}

As you can see here, the test case result can have in some situations multiple testOutputs, defined as a list of map.

What I want is to write for every map entry in the testOutput, a seperate row in the csv file. But with the code I wrote below, only the first entry of the testOutput is written as only one row in the csv file.

 public String convertDmnRuleTestResultToCsv(DmnRuleTestResult result) {
    List<DmnTestCaseResult> results = result.getTestCases();
    try(StringWriter sw = new StringWriter(); CSVWriter writer = new CSVWriter(sw, CSVWriter.DEFAULT_SEPARATOR, CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.NO_ESCAPE_CHARACTER, CSVWriter.DEFAULT_LINE_END)) {
        StatefulBeanToCsv<DmnTestCaseResult> beanToCsv = new StatefulBeanToCsvBuilder<DmnTestCaseResult>(writer)
                .withApplyQuotesToAll(false)
                .build();
        beanToCsv.write(results);
        return sw.toString();
    } catch(Exception ex){
        throw new CsvParseException(ex.getMessage());
    }
}

How can I tell the OpenCsv that it should create seperate row for each entry in the testOutputs ?

EDIT: Added more information

UI: enter image description here

Resulted incorrect CSV: enter image description here

Expected correct CSV: enter image description here

As you can see from the screenshots, one input can have multiple test outputs. Therefore I want to create for every test output a seperate line in csv file.

CodePudding user response:

As StatefulBeanToCsv does not seem to be capable to generating multiple lines for a single bean, I suggest implementing a custom mapping function. This also requires you to manually print the header line as well.

public static String convertDmnRuleTestResultToCsv(DmnRuleTestResult result) {
    List<DmnTestCaseResult> results = result.getTestCases();
    try (StringWriter sw = new StringWriter();
            CSVWriter writer = new CSVWriter(sw, CSVWriter.DEFAULT_SEPARATOR,
                    CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.NO_ESCAPE_CHARACTER,
                    CSVWriter.DEFAULT_LINE_END)) {
        writeHeader(writer);
        for (DmnTestCaseResult r : results) {
            for (Map<String, Object> map : r.getTestOutput())
                writer.writeNext(map(r, map));
        }
        return sw.toString();
    } catch (Exception ex) {
        throw new RuntimeException(ex.getMessage());
    }
}

private static void writeHeader(CSVWriter writer) {
    List<String> header = new ArrayList<>();
    header.add("ERRORMESSAGE");
    header.add("EXPECTEDOUTPUT");
    header.add("INPUT");
    header.add("OUTPUT");
    writer.writeNext(header.toArray(new String[] {}));

}

private static String[] map(DmnTestCaseResult r, Map<String, Object> testOutput) {
    // you can manually adjust formats here as well; entrySet() call can be left out, it does change the format. do what you like more
    List<String> line = new ArrayList<>();
    line.add(r.getErrorMessage());
    line.add(r.getExpectedOutput().entrySet().toString());
    line.add(r.getTestInput().entrySet().toString());
    line.add(testOutput.entrySet().toString());
    return line.toArray(new String[] {});
}

And this prints:

ERRORMESSAGE,EXPECTEDOUTPUT,INPUT,OUTPUT
errorMessage,[expectedOutput1=expectedOutput1, expectedOutput2=expectedOutput2],[input2=testInput2, input1=testInput1],[testOut2=testOut2, testOut=testOut1]
errorMessage,[expectedOutput1=expectedOutput1, expectedOutput2=expectedOutput2],[input2=testInput2, input1=testInput1],[testOut3=testOut3, testOut4=testOut4]
  • Related