Question

I have a class A which has few properties:

class A {

private String col1;
private String col2;
private List<B> bList;

// setters and getters
}

Class B {
private String b1;
private String b2;
//setters and getters
}

I am trying to write this into a csv file using supercsv and dozer.

The csv should have the same number of rows as elements in the list bList from class A. And should have four columns col1, col2 (which would be common for all the rows) and b1, b2 from the list of class B in class A.

Currently in the mapping file I have:

<mapping type="one-way" wildcard="false" map-null="true">
                <class-a>package.a</class-a>
                <class-b>org.supercsv.io.dozer.CsvDozerBeanData</class-b>
.....
</mapping>

But it only maps the data into one row. What would be a better way to do it without mapping it into a flatter class. In the mappings can be map it from class A to a list of CsvDozerBeanData? Will that work?

Thanks.

Was it helpful?

Solution

Can you add a relationship from B back to A? Then you could iterate over each List of children with the following bean mapping:

new String[] { "a.col1", "a.col2", "b1", "b2" }

Here's an example of how you'd use this. Note the nested loops to iterate over each B inside each A.

public class DozerTest {

    @Test
    public final void test() throws IOException {
        StringWriter out = new StringWriter();
        ICsvDozerBeanWriter writer = new CsvDozerBeanWriter(out, 
                CsvPreference.STANDARD_PREFERENCE);
        writer.configureBeanMapping(B.class, 
                new String[] { "a.col1", "a.col2", "b1", "b2" });

        for (A a : generateData()) {
            for (B b : a.getbList()) {
                writer.write(b);
            }
        }
        writer.flush();
        System.out.println(out.toString());
    }

    private List<A> generateData() {
        List<A> data = new ArrayList<A>();

        for (int i = 0; i < 3; i++) {
            A a = new A();
            a.setCol1("col1 for a" + i);
            a.setCol2("col2 for a" + i);

            B firstB = new B();
            firstB.setB1("first b1 for a" + i);
            firstB.setB2("first b2 for a" + i);
            firstB.setA(a);

            B secondB = new B();
            secondB.setB1("second b1 for a" + i);
            secondB.setB2("second b2 for a" + i);
            secondB.setA(a);

            a.setbList(Arrays.asList(firstB, secondB));
            data.add(a);
        }

        return data;
    }

}

This prints:

col1 for a0,col2 for a0,first b1 for a0,first b2 for a0
col1 for a0,col2 for a0,second b1 for a0,second b2 for a0
col1 for a1,col2 for a1,first b1 for a1,first b2 for a1
col1 for a1,col2 for a1,second b1 for a1,second b2 for a1
col1 for a2,col2 for a2,first b1 for a2,first b2 for a2
col1 for a2,col2 for a2,second b1 for a2,second b2 for a2

Update to address comments

If you can't add a relationship, then you can just use CellProcessors (and you don't even need to use Dozer). I've just created 3 simple cell processors:

  • ElementAt - just grabs the element at the desired index
  • GetB1 - gets the b1 field of a B
  • GetB2 - gets the b2 field of a B

And here they are in action:

@Test
public final void test2() throws IOException {
    StringWriter out = new StringWriter();
    ICsvDozerBeanWriter writer = new CsvDozerBeanWriter(out, 
        CsvPreference.STANDARD_PREFERENCE);
    writer.configureBeanMapping(A.class, 
        new String[] { "col1", "col2", "bList", "bList" });

    for (A a : generateData()) {
        for (int i = 0; i < a.getbList().size(); i++) {
            CellProcessor[] processors = new CellProcessor[] { null, null, 
                new ElementAt(i, new GetB1()),
                new ElementAt(i, new GetB2()) };
            writer.write(a, processors);
        }
    }
    writer.flush();
    System.out.println(out.toString());
}

class GetB1 extends CellProcessorAdaptor {
    public Object execute(Object value, CsvContext context) {
        validateInputNotNull(value, context);
        return ((B) value).getB1();
    }
}

class GetB2 extends CellProcessorAdaptor {
    public Object execute(Object value, CsvContext context) {
        validateInputNotNull(value, context);
        return ((B) value).getB2();
    }
}

class ElementAt extends CellProcessorAdaptor {

    private final int index;

    public ElementAt(int index) {
        this.index = index;
    }

    public ElementAt(int index, CellProcessor next) {
        super(next);
        this.index = index;
    }

    public Object execute(Object value, CsvContext context) {
        validateInputNotNull(value, context);
        Object element = ((List<?>) value).get(index);
        return next.execute(element, context);
    }

}

Output:

col1 for a0,col2 for a0,first b1 for a0,first b2 for a0
col1 for a0,col2 for a0,second b1 for a0,second b2 for a0
col1 for a1,col2 for a1,first b1 for a1,first b2 for a1
col1 for a1,col2 for a1,second b1 for a1,second b2 for a1
col1 for a2,col2 for a2,first b1 for a2,first b2 for a2
col1 for a2,col2 for a2,second b1 for a2,second b2 for a2

p.s. I wouldn't recommend supplying your own dozer mapping XML unless you really have to (and in this case it doesn't seem necessary).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top