问题
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.
回答1:
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 indexGetB1
- gets theb1
field of aB
GetB2
- gets theb2
field of aB
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).
来源:https://stackoverflow.com/questions/21862624/supercsv-dozer-writing-to-a-csv-file-for-object-with-a-list-into-multiple-row