javaspring-annotationsopencsvcompile-timecompile-time-constant

CsvBindByPosition : value for annotation attribute must be a constant expression


I am trying to read a CSV file whose header position numbers comes from a property file. I get the position number for the fields using @Value. But however I am unable to bind this value as the position for @CsvBindByPosition.

Here is my code :

public class MyPojo {

    @Value(value = "${csv.pojo.refNumber}")
    public static final int test;

    @CsvBindByPosition(position = test)
    private String id;
}

This gives me this error:

The value for annotation attribute CsvBindByPosition.position must be a constant expression

Is there a way to resolve this as my position needs to be read from a property file itself?


Solution

  • As suggested by MWiesner answer, annotation value can't be set in runtime.

    What we need is some way to configure the column position mapping in runtime instead.

    import com.opencsv.CSVReader;
    import com.opencsv.CSVReaderBuilder;
    import com.opencsv.bean.ColumnPositionMappingStrategy;
    import com.opencsv.bean.ColumnPositionMappingStrategyBuilder;
    import com.opencsv.bean.CsvToBean;
    import com.opencsv.bean.CsvToBeanBuilder;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import java.io.StringReader;
    
    @SpringBootTest(
            classes = CsvByPositionInRuntimeTest.Pojo.class,
            properties = {"csv.pojo.refNumber=0", "csv.pojo.name=1"}
    )
    public class CsvByPositionInRuntimeTest {
        // assume 0
        @Value("${csv.pojo.refNumber}")
        private Integer refNumberIndex;
        // assume 1
        @Value("${csv.pojo.name}")
        private Integer nameIndex;
    
        // or specify column order like 'refNumber,name'
        // this is more readable and easy to maintain
        @Value("${csv.pojo.columnOrder}")
        private String[] columnOrder;
    
        @Test
        public void parse() {
            CSVReader csvReader = new CSVReaderBuilder(
                    new StringReader("""
                            123,david
                            456,terry
                            """)
            ).build();
            ColumnPositionMappingStrategy<Pojo> positionMappingStrategy = new ColumnPositionMappingStrategyBuilder<Pojo>().build();
            // this is just for demo, proper implementation is need for all column index
            positionMappingStrategy.setColumnMapping(refNumberIndex < nameIndex ? new String[]{"refNumber", "name"} : new String[]{"name", "refNumber"});
            // positionMappingStrategy.setColumnMapping(columnOrder);
            positionMappingStrategy.setType(Pojo.class);
            CsvToBean<Pojo> csvModelCsvToBean =
                    new CsvToBeanBuilder<Pojo>(csvReader).withMappingStrategy(
                            positionMappingStrategy
                    ).build();
            csvModelCsvToBean.parse().forEach(m -> System.out.println("id:%s ,name:%s".formatted(m.refNumber, m.name)));
        }
    
        public static class Pojo {
            private String refNumber;
            private String name;
        }
    }