Apache Camel 2.17.3 - Exception unmarshalling CSV stream with bindy - apache-camel

I have written a simple route to read CSV file and save it in a new file in JSON format.
When I try to split and stream the body the unmarshal breaks with ".IllegalArgumentException: No records have been defined in the CSV".
However, it works well without split and streaming!
Unmarshal uses a BindyCsvDataFormat and a CustomCsvRecord defines the fields.
CSV Sample:
HEADER_1;HEADER_2;HEADER_3;HEADER_4;HEADER_5
data11;data12;data13;data14;data15
data21;data22;data23;data24;data25
Can you help me understand is this the correct behaviour and if so how can I control reading large files?
Please refer below:
public class MyRouteBuilder extends RouteBuilder {
public void configure() {
BindyCsvDataFormat bindy = new BindyCsvDataFormat(com.demo.camel.CustomCsvRecord.class);
from("file://data?move=../completed/&include=.*.csv&charset=UTF-8")
.log("Reading file..")
// .split(body().tokenize("\n")).streaming()
// .throttle(2)
// .timePeriodMillis(3000)
.unmarshal(bindy)
.marshal().json(true)
.log("writing to file")
.to("file://target/messages?fileExist=Append");
}
}
#CsvRecord(separator = ";", skipFirstLine = true )
public class CustomCsvRecord implements Serializable{
private static final long serialVersionUID = -1537445879742479656L;
#DataField(pos = 1)
private String header_1;
#DataField(pos = 2)
private String header_2;
#DataField(pos = 3)
private String header_3;
#DataField(pos = 4)
private String header_4;
#DataField(pos = 5)
private String header_5;
public String getHeader_1() {
return header_1;
}
public void setHeader_1(String header_1) {
this.header_1 = header_1;
}
public String getHeader_2() {
return header_2;
}
public void setHeader_2(String header_2) {
this.header_2 = header_2;
}
public String getHeader_3() {
return header_3;
}
public void setHeader_3(String header_3) {
this.header_3 = header_3;
}
public String getHeader_4() {
return header_4;
}
public void setHeader_4(String header_4) {
this.header_4 = header_4;
}
public String getHeader_5() {
return header_5;
}
public void setHeader_5(String header_5) {
this.header_5 = header_5;
}
}

Could it be that you have set skipFirstLine = true ? But since you split with line break, skipping the first line means there are no lines to parse the CSV. Try this instead .split().tokenize("\n", 1000).streaming(). This basically means we want to split based on the token "\n" and we want to group N number of lines together. In this case it is 1000 so it will at the most group 1000 lines together in a split.
So if you send 10 000 rows it will split them in 10 chunks.
Now the issue is if you have skipFirstLine set it will skip the first line. Since you were previously splitting each line, when it came to the CSV parser it would skip that line since that is what it was told to do. So, then there is nothing to parse and it complained there are no records.
The problem now is that what happens after you split by say every 1000 rows and you get 10 000 rows. Will it remove the first line in every split chunk? I would suspect so. I would think the best thing is to add a a processor before the split. Convert the body to a byte[]. Search for the first "\n" and simply delete that row or get the byteArray after that index. Then you can do normal split and remove skipFirstRow.
Also, your output is in list but that is due to your mapping.

Related

How do I call this list from another class in C#?

I am still new to the whole C# thing but I found this code posted from grovesNL about 5 years ago which I believe will work.
namespace DataAccessClass
{
public class FileReader
{
static void Main(string[] args)
{
List<DailyValues> values = File.ReadAllLines("C:\\Users\\Josh\\Sample.csv")
.Skip(1)
.Select(v => DailyValues.FromCsv(v))
.ToList();
}
}
public class DailyValues
{
DateTime Date;
decimal Open;
decimal High;
decimal Low;
decimal Close;
decimal Volume;
decimal AdjClose;
public static DailyValues FromCsv(string csvLine)
{
string[] values = csvLine.Split(',');
DailyValues dailyValues = new DailyValues();
dailyValues.Date = Convert.ToDateTime(values[0]);
dailyValues.Open = Convert.ToDecimal(values[1]);
dailyValues.High = Convert.ToDecimal(values[2]);
dailyValues.Low = Convert.ToDecimal(values[3]);
dailyValues.Close = Convert.ToDecimal(values[4]);
dailyValues.Volume = Convert.ToDecimal(values[5]);
dailyValues.AdjClose = Convert.ToDecimal(values[6]);
return dailyValues;
}
}
}
I am trying to read a csv file skipping the header and get it into a list that is accessible from another class. So my Architecture is DataAccessClass that has a class called FileReader and a class called Values. My task is to read this csv file into class FileReader and then to create an object list to hold it in the class Values. When I go to the Values class to call it I can't figure it out. This is how I am trying to call it. It is saying DailyValues.FromCsv(string) is a method that is not valid.
public List<string> GetList()
{
return DataAccessClass.DailyValues.FromCsv.dailyValues;
}
I want to be able to access this list further up the stack.
Your expression DataAccessClass.DailyValues.FromCsv.dailyValues is the culprit.
DataAccessClass.DailyValues.FromCsv is valid, and references the static method named FromCsv in the class DataAccessClass.DailyValues. But then going on by adding .dailyValues is incorrect. It is a method, nothing to peek into and extract stuff using ..
You could (if that was the intention) call the function, and directly work with the result:
DataAccessClass.DailyValues.FromCsv(some_csv_string) is an expression of type DailyValues. There you could then access - as an example - 'High' with:
DailyValues dv;
dv = DataAccessClass.DailyValues.FromCsv(some_csv_string);
dosomething(dv.High);
But for that to work, High would have to have the visibility of public.

Flink output selector has strange behavior

I have a stream with two forks, thus two SplitStreams.
Here is the code :
static final class MyOutputSelector1 implements OutputSelector<Long> {
#Override
public Iterable<String> select(Long value) {
List<String> outputs = new ArrayList<>();
if (value < 5) {
outputs.add("valid1");
}
else {
outputs.add("error1");
}
return outputs;
}
}
static final class MyOutputSelector2 implements OutputSelector<Long> {
private static final long serialVersionUID = 1L;
#Override
public Iterable<String> select(Long value) {
List<String> outputs = new ArrayList<String>();
if (value == 2) {
outputs.add("valid2");
}
else {
outputs.add("error2");
}
return outputs;
}
}
#Test
public void outputSelectorTest() throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
SplitStream<Long> split1 = env.generateSequence(1, 11).split(new MyOutputSelector1());
DataStream<Long> stream11 = split1.select("valid1");
stream11.print();
SplitStream<Long> split2 = stream11.split(new MyOutputSelector2());
DataStream<Long> stream21 = split2.select("valid2");
stream21.print();
DataStream<Long> stream22 = split2.select("error2");
stream22.printToErr();
env.execute();
}
And here is the input I get when I execute this code :
Program output
My source is a list of integers between 1 and 11.
I expect stream11 to contain only integers less than 5. Which seems to be ok when I print it.
I expect stream21 to contain 2, which seems to be the case as two "2" are printed.
However, I would expect stream22 to contain all integers less than 5 except two but all integers between 1 and 11 are printed.
Why does it behave like that? I thought the first selector would have kept only integers from 1 to 4 in the stream but integers from 5 to 11 reappears after the last split...
To sum up, here is what I get and what I expect :
Diagram
There is probably a mechanism I do not understand. Is there any solution ? Should I use filters instead ?
Thanks.
It looks like you found a bug. I could reproduce the issue with Flink 1.1.3 and the current master branch (Flink 1.2-SNAPSHOT).
I filed a JIRA issue: FLINK-5031 to track the bug.

Sending a complex object as a parameter in Resteasy

I am using resteasy, and till now I am just sending and receiving string as parameters and every thing was OK, but now I want to send a complex object ( List<Map<String, ObjectVal>> ) as one of my parameters. My objectVal class has two simple field ( id and value, with getters and setters).
I can find different question and answers for sending objects as parameters but all of them are missing something and not useful for me.
here is my functions with a simple string parameter
#GET
#Path("/isUserAuthorizedToDocument")
public Response isUserAuthorizedToDocumentService(
#QueryParam("userID") String userID){
.............
.............
}
and the client
private ClientRequest req =new ClientRequest(....url with path and ....)
req.queryParameter("userID", user.getUserId());
ClientResponse<Boolean> response = req.get(Boolean.class);
Now I want to send a parameter from my client in the form of List<Map<String,ObjectVal>> and recieve it in my rest function.
My ObjectVal class
#XmlRootElement(name = "objectValueDTO")
public class ObjectValueDTO implements Serializable {
/**
* Id for this class
*/
private static final long serialVersionUID = 164186789404269392L;
// Id on object type
private String objectTypeID = "";
// Selection
private String value = "";
/** Getter and Setters */
#XmlElement
public String getObjectTypeID() {
return objectTypeID;
}
public void setObjectTypeID(String objectTypeID) {
this.objectTypeID = objectTypeID;
}
#XmlElement
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
any help will be appreciated
I may be niave on this. But when you have to send complex parameters, you need to use PUT and send the parameters in the request.

How to deserialize several nested Json arrays (more than 2) using DataContractJsonSerializer (Windows Phone 7)

I need to deserialize the next Json string that has several nested Json arrays:
{"d1":[["11791452",[["980",""]]],["11791453",[["1060",""],["1140",""],["1220",""],["1300",""]]],["11791454",[["1070",""]]]]}
I try to do it in several steps, so far I'm able to deserialize three levels of nested arrays. As follow:
{"aOaOa":[[["1060",""],["1140",""],["1220",""],["1300",""]]]}
public class ThreeSimpleNestedArrays
{
public List<List<string[]>> aOaOa; //array of arrays of arrays
public ThreeSimpleNestedArrays()
{
aOaOa = new List<List<string[]>>();
}
}
But the problem arise when I add the extra string in the array structure:
{"TEST": [["11791453",[["1060",""],["1140",""],["1220",""],["1300",""]]],["123456",[["0","1"],["2","3"]]]]}
public class ComplexNestedArray
{
public List<Dictionary<string,List<string[]> >> TEST;
public ComplexNestedArray()
{
TEST = new List<Dictionary<string, List<string[]>>>();
}
}
I'm getting the next error message:
"Unable to cast object of type 'System.String' to type 'System.Collections.Generic.Dictionary`2[System.String,System.Object]'."
What am I missing?
Can anybody suggest a way to deserialize an object like this nested within Json arrays using DataContractJsonSerializer?
The code I use to deserialize is the next:
//Works
DataContractJsonSerializer dcJsonSer = new DataContractJsonSerializer(typeof(ThreeSimpleNestedArrays));
ThreeSimpleNestedArrays root = (ThreeSimpleNestedArrays)dcJsonSer.ReadObject(str);
//Don't work
DataContractJsonSerializer dcJsonSer = new DataContractJsonSerializer(typeof(ComplexNestedArray));
ComplexNestedArray root = (ComplexNestedArray)dcJsonSer.ReadObject(str);
Btw, I'm able to deserilize the object when it is serilized as a Json Object as follow:
{"TEST": [{"s": "11791453","aOa": [["1060",""],["1140",""],["1220",""],["1300",""]]},{"s": "123456","aOa":[["0","1"],["2","3"]]}]}
using a class with two members (a string "s" and a List of string[] "aOa"), but without the names, when the object is serialized as an array, I'm unable to do it.
Any suggestion?
Ok, it looks like the DataContractJsonSerializer is smarter than I though .
It turns out that the way to deserialize that kid of nested objects array is with a class like this:
public class ComplexNestedArray
{
//{"TEST": [["11791453",[["1060",""],["1140",""],["1220",""],["1300",""]]],["123456",[["0","1"],["2","3"]]]]}
public List<List<object>> TEST { get; set; }
}
After that, it is only a mater to do a couple of for cycles and casts to the appropriate class structure.
Btw, This is a MUST in your toolbox in case you have to deal with Json:
json2csharp
Here is my solution. However I'll try to add later a way for your full json:
class Program {
static void Main(string[] args) {
new Program();
}
public Program() {
string full = "{\"d1\":[[\"11791452\",[[\"980\",\"\"]]],[\"11791453\",[[\"1060\",\"\"],[\"1140\",\"\"],[\"1220\",\"\"],[\"1300\",\"\"]]],[\"11791454\",[[\"1070\",\"\"]]]]}";
string simple1 = "{\"aOa\":[[\"1060\",\"\"],[\"1140\",\"\"],[\"1220\",\"\"],[\"1300\",\"\"]]}";
string simple2 = "{\"aOaOa\":[[[\"1060\",\"\"],[\"1140\",\"\"],[\"1220\",\"\"],[\"1300\",\"\"]]]}";
DataContractJsonSerializer d1 = new DataContractJsonSerializer(typeof(S1));
S1 r1 = (S1)d1.ReadObject(new MemoryStream(Encoding.Default.GetBytes(simple1)));
DataContractJsonSerializer d2 = new DataContractJsonSerializer(typeof(S2));
S2 r2 = (S2)d2.ReadObject(new MemoryStream(Encoding.Default.GetBytes(simple2)));
Console.WriteLine("done");
Console.ReadKey();
}
[DataContract]
class S1 {
[DataMember]
List<List<String>> aOa;
}
[DataContract]
class S2 {
[DataMember]
List<List<List<string>>> aOaOa;
}
}

Using Sencha GXT 3, generate a line chart populated with a dynamic number of line series fields?

Using Sencha GXT 3.0 is it possible to generate a line chart and populate it with a dynamic number of line series fields, and if so, what is the recommended method?
I know multiple series fields can be added to a chart, but the line chart examples (and the other chart examples for that matter) make use of an interface which extends PropertyAccess<?> and the interface specifies a static number of expected fields (e.g. data1(), data2(), data3(), etc.). If the interface is to be used to specify the fields to add to the chart, how could you account for a chart which may require n number of fields (i.e. n number of line series on a given chart).
Example provided on Sencha's site:
http://www.sencha.com/examples/#ExamplePlace:linechart
I ran into the same issue. It would be a much nicer design if each series had a store instead of having one store per chart.
I had one long list of metric values in metricDataStore. Each metric value has a description. I wanted all the metric values with the same description displayed on one (and only one) series. I had my value providers for each series return null for both the x and y axis if the value wasn't supposed to be in the series.
This seems like a hack to me but it works for my usage:
myChart = new Chart<MetricData>();
myChart.setStore(metricDataStore);
.
.
.
for (MetricInfo info : metricInfoData) {
LineSeries<MetricData> series = new LineSeries<MetricData>();
series.setChart(myChart);
series.setSmooth(false);
series.setShownInLegend(true);
series.setHighlighting(true);
series.setYAxisPosition(Chart.Position.LEFT);
series.setYField(new MetricValueProvider(info.getName()));
series.setXAxisPosition(Chart.Position.BOTTOM);
series.setXField(new MetricTimeProvider(info.getName()));
myChart.addSeries(series);
}
.
.
.
private class MetricTimeProvider extends Object implements ValueProvider<MetricData, Long> {
private String metricName;
public MetricTimeProvider(String metricName) {
this.metricName = metricName;
}
#Override
public Long getValue(MetricData m) {
if (metricName != null && metricName.equals(m.getLongDesc()))
return m.getId();
else
return null;
}
#Override
public void setValue(MetricData m, Long value) {
}
#Override
public String getPath() {
return null;
}
}
private class MetricValueProvider extends Object implements ValueProvider<MetricData, Double> {
private String metricName;
public MetricValueProvider(String metricName) {
this.metricName = metricName;
}
#Override
public Double getValue(MetricData m) {
if (metricName != null && metricName.equals(m.getLongDesc()))
return m.getMetricValue();
else
return null;
}
#Override
public void setValue(MetricData m, Double value) {
}
#Override
public String getPath() {
return null;
}
}

Resources