0

我有一条路线,它假设读取一个巨大的 XML 文件,然后编写一个带有标题的 CSV 文件。XML 记录需要先进行转换,所以我将其映射到 java POJO,然后再次编组以写入 csv 文件。

我无法加载内存中的所有记录,因为该文件包含更多 200k 记录。

问题:我只看到最后一条记录被添加到 CSV 文件中。不知道为什么它没有将数据附加到现有文件中。

任何想法如何使它工作。CSV 中需要标头。我没有看到任何其他选项可以直接转换流并将标头连同 CSV 一起写入,而无需先将其解组到 Pojo。我也尝试使用 BeanIO,这需要我添加一个 Header 记录,但不确定如何将其注入到流中。

   from("{{xml.files.route}}")

            .split(body().tokenizeXML("EMPLOYEE", null))
            .streaming()
            .unmarshal().jacksonXml(Employee.class)

            .marshal(bindyDataFormat)

            .to("file://C:/Files/Test/emp/csv/?fileName=test.csv")

            .end();

如果我尝试附加到现有文件中,那么 CSV 文件会将标题附加到每个记录迭代中。

 .to("file://C:/Files/Test/emp/csv/?fileName=test.csv&fileExist=append")
4

1 回答 1

0

您的问题与camel-bindy而不是文件组件有关。它有点希望您编组集合对象而不是单个对象,因此如果您单独编组每个对象并@CsvRecord(generateHeaderColumns = true )在您的 Employee 类上拥有,那么每次编组单个 Employee 对象时都会获得标题。

您可以设置generateHeaderColumns为 false 并手动使用标题字符串启动文件。获取 Bindy 注释类的标题的一种方法是使用org.apache.commons.lang3.reflect.FieldUtilsapache-commons 获取使用 DataField 注释的字段,并根据位置、columnName 和 fieldName 构造标题字符串。

当我需要将某些内容流式传输到文件时,我通常更喜欢骆驼流而不是文件组件,但是使用带有附加的文件组件可能也可以。

例子:

package com.example;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.dataformat.bindy.annotation.DataField;
import org.apache.camel.dataformat.bindy.csv.BindyCsvDataFormat;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.junit.Test;

public class ExampleTest  extends CamelTestSupport {
    
    @Test
    public void testStreamEmployeesToCsvFile(){
        
        List<Employee> body = new ArrayList<>();
        body.add(new Employee("John", "Doe", 1965));
        body.add(new Employee("Mary", "Sue", 1987));
        body.add(new Employee("Gary", "Sue", 1991));

        template.sendBody("direct:streamEmployeesToCSV", body);
    }

    @Override
    protected RoutesBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder(){

            @Override
            public void configure() throws Exception {
                
                BindyCsvDataFormat csvDataFormat = new BindyCsvDataFormat(Employee.class);
                System.out.println(getCSVHeadersForClass(Employee.class, ","));

                from("direct:streamEmployeesToCSV")
                    .setProperty("Employees", body())
                    // a bit hacky due to camel writing first entry and headers 
                    // on the same line for some reason with (camel 2.25.2)
                    .setBody().constant("")
                    .to("file:target/testoutput?fileName=test.csv&fileExist=Override")
                    .setBody().constant(getCSVHeadersForClass(Employee.class, ","))
                    .to("stream:file?fileName=./target/testoutput/test.csv")
                    .split(exchangeProperty("Employees"))
                        .marshal(csvDataFormat)
                        .to("stream:file?fileName=./target/testoutput/test.csv")
                    .end()
                    .log("Done");
            }

            private String getCSVHeadersForClass(Class clazz, String separator ) {

                Field[] fieldsArray = FieldUtils.getFieldsWithAnnotation(clazz, DataField.class);
                List<Field> fields = new ArrayList<>(Arrays.asList(fieldsArray));

                fields.sort(new Comparator<Field>(){

                    @Override
                    public int compare(Field lhsField, Field rhsField) {

                        DataField lhs = lhsField.getAnnotation(DataField.class);
                        DataField rhs = rhsField.getAnnotation(DataField.class);

                        return lhs.pos() < rhs.pos() ? -1 : (lhs.pos() > rhs.pos()) ? 1 : 0;
                    }
                });

                String[] fieldHeaders = new String[fields.size()];
                for (int i = 0; i < fields.size(); i++) {
                    DataField dataField = fields.get(i).getAnnotation(DataField.class);

                    if(dataField.columnName().equals(""))
                        fieldHeaders[i] = fields.get(i).getName();
                    else
                        fieldHeaders[i] = dataField.columnName();
                }
                
                String csvHeaders = "";
                for (int i = 0; i < fieldHeaders.length; i++) {
                    csvHeaders += fieldHeaders[i];
                    csvHeaders += i < fieldHeaders.length - 1 ? separator : "";
                }
                return csvHeaders;
            }
        };
    }
}
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>${apache-commons.version}</version>
</dependency>
于 2022-02-14T12:07:36.427 回答