theo
theo

Reputation: 995

Camel bindy marshal to file creates multiple header row

I have the following camel route:

from(inputDirectory)
  .unmarshal(jaxb)
  .process(jaxb2CSVDataProcessor)
  .split(body()) //because there is a list of CSVRecords
  .marshal(bindyCsvDataFormat)
  .to(outputDirectory); //appending to existing file using "?autoCreate=true&fileExist=Append"

for my CSV model class I am using annotations:

@CsvRecord(separator = ",", generateHeaderColumns = true)
...

and for properties

@DataField(pos = 0)
...

My problem is that the headers are appended every time a new csv record is appended.

Is there a non-dirty way to control this? Am I missing anything here?

Upvotes: 0

Views: 1616

Answers (3)

Stephen Lawson
Stephen Lawson

Reputation: 1

Hope this helps anyone else. I needed to do something similar where after my first split message I wanted to supress the header output. Here is a complete class (the 'FieldUtils' is part of the apache commons lib)

package com.routes;

import java.io.OutputStream;

import org.apache.camel.Exchange;
import org.apache.camel.dataformat.bindy.BindyAbstractFactory;
import org.apache.camel.dataformat.bindy.BindyCsvFactory;
import org.apache.camel.dataformat.bindy.BindyFactory;
import org.apache.camel.dataformat.bindy.FormatFactory;
import org.apache.camel.dataformat.bindy.csv.BindyCsvDataFormat;
import org.apache.commons.lang3.reflect.FieldUtils;

public class StreamingBindyCsvDataFormat extends BindyCsvDataFormat {

    public StreamingBindyCsvDataFormat(Class<?> type) {
        super(type);
    }

    @Override
    public void marshal(Exchange exchange, Object body, OutputStream outputStream) throws Exception {
        final StreamingBindyModelFactory factory = (StreamingBindyModelFactory) super.getFactory();
        final int splitIndex = exchange.getProperty(Exchange.SPLIT_INDEX, -1, int.class);
        final boolean splitComplete = exchange.getProperty(Exchange.SPLIT_COMPLETE, false, boolean.class);

        super.marshal(exchange, body, outputStream);

        if (splitIndex == 0) {
        factory.setGenerateHeaderColumnNames(false); // turn off header generate after first exchange
        } else if(splitComplete) {
        factory.setGenerateHeaderColumnNames(true); // turn on header generate when split complete
        }
    }

    @Override
    protected BindyAbstractFactory createModelFactory(FormatFactory formatFactory) throws Exception {
        BindyCsvFactory bindyCsvFactory = new StreamingBindyModelFactory(getClassType());
        bindyCsvFactory.setFormatFactory(formatFactory);
        return bindyCsvFactory;
    }

    public class StreamingBindyModelFactory extends BindyCsvFactory implements BindyFactory {

        public StreamingBindyModelFactory(Class<?> type) throws Exception {
            super(type);
        }

        public void setGenerateHeaderColumnNames(boolean generateHeaderColumnNames) throws IllegalAccessException {
            FieldUtils.writeField(this, "generateHeaderColumnNames", generateHeaderColumnNames, true);
        }

    }

}

Upvotes: 0

Viktor Stoitschev
Viktor Stoitschev

Reputation: 402

I made a work around which is working quite nicely, creating the header by querying the columnames of the @DataField annotation. This is happening once the first time the file is written. I wrote down the whole solution here:

How to generate a Flat file with header and footer using Camel Bindy

Upvotes: 1

theo
theo

Reputation: 995

I ended up adding a processor that checks if the csv file exists just before the "to" clause. In there I do a manipulation of the byte array and remove the headers.

Upvotes: 0

Related Questions