Unmarshal a CSV file with Apache Camel
Von Carsten
Today I tried to work with Apache Camel for the first time. I’m currently reading the book Camel in Action and want to try the example with data that has a meaning to me. I decided to follow the examples of Chapter 3: Transforming data with Camel and try to read rows from a CSV file and convert them with Bindy into annotated POJOs.
But, oh boy, is the documentation lacking. And the book is not helping me at all at this point. The examples there look easy and when you’re reading the book you can follow the thoughts. But when I actually tried to write my first unit test without pasting the whole examples into your project, I got lost. The documentation of Bindy is very confusing if you only start writing tests for Camel. And the examples tests they show in the docs are so complicated I couldn’t strip them down to a minimal version that would just read the CSV and unmarshal the rows into POJOs.
And what do you do if you can’t figure out how to do stuff? You post a question on StackOverflow.
And right after I posted that question, I got the test running. Isn’t it always like that? As soon as you ask for help, you find the solution to your problem. As if I’m afraid to let others see how much I suck. Anyway, here is a working junit test that will unmarshal a CSV into POJOs with the use of an annotated java bean:
CsvBean.java
package de.kopis.camel.model;
import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
import org.apache.camel.dataformat.bindy.annotation.DataField;
@CsvRecord(separator = ",")
public class CsvBean {
@DataField(pos = 1)
private String first;
@DataField(pos = 2)
private String second;
@DataField(pos = 3)
private String third;
@DataField(pos = 4)
private String fourth;
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getSecond() {
return second;
}
public void setSecond(String second) {
this.second = second;
}
public String getThird() {
return third;
}
public void setThird(String third) {
this.third = third;
}
public String getFourth() {
return fourth;
}
public void setFourth(String fourth) {
this.fourth = fourth;
}
}
CsvToBeanWithBindyTest.java
package de.kopis.camel;
import java.util.List;
import java.util.Map;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.dataformat.bindy.csv.BindyCsvDataFormat;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
import de.kopis.camel.model.CsvBean;
public class CsvToBeanWithBindyTest extends CamelTestSupport {
@Test
public void testCsv() throws Exception {
MockEndpoint mock = getMockEndpoint("mock:queue.csv");
mock.expectedMessageCount(1);
assertMockEndpointsSatisfied();
List csv = (List) mock.getReceivedExchanges().get(0).getIn().getBody();
Map map1 = (Map) csv.get(0);
CsvBean csv1 = (CsvBean) map1.get("de.kopis.camel.model.CsvBean");
assertEquals("row 01", csv1.getFirst());
Map map2 = (Map) csv.get(1);
CsvBean csv2 = (CsvBean) map2.get("de.kopis.camel.model.CsvBean");
assertEquals("row 11", csv2.getFirst());
}
@Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
@Override
public void configure() throws Exception {
context.setTracing(true);
from("file://src/test/resources?noop=true&fileName=test.csv")
.unmarshal(new BindyCsvDataFormat("de.kopis.camel.model"))
.to("mock:queue.csv");
}
};
}
}
You can get the complete example in this gist.
The confusing thing is that I don’t get a list of CsvBean
s, but a list of Map
s that have an entry with the key set to the name of my class and the value set to the actual unmarshalled CsvBean
. That was a surprise, and I don’t know yet what that means. I think there’s more to the CSV than I can see now, maybe having multiple bean classes unmarshalled from the CSV when there is a different number of columns in the rows.
HTH.