대문자 헤더 없이 OpenCsv로 csv 파일을 작성하고 선언 순서를 따릅니다.

16860 단어 javaopencsv
(OpenCSV 5.6 사용)

문제



이 클래스는 각 CSV 행을 설명합니다. 열이 2개뿐입니다. id는 첫 번째 열이고 country_code는 두 번째 열입니다.

public class CsvRow {

    @CsvBindByName(column = "id")
    private String id;

    @CsvBindByName(column = "country_code")
    private String countryCode;

    public CsvRow(String id, String countryCode) {
        this.id = id;
        this.countryCode = countryCode;
    }

}


CSV 파일은 StatefulBeanToCsv에 의해 생성됩니다.

public class Application {
    public static void main(String[] args) throws Exception {
        CsvRow[] csvRows = new CsvRow[] {
                new CsvRow("HK", "852"),
                new CsvRow("US", "1"),
                new CsvRow("JP", "81"),
        };

        Path outputPath = Path.of("countries.csv");
        try (var writer = Files.newBufferedWriter(outputPath)) {
            StatefulBeanToCsv<CsvRow> csv = new StatefulBeanToCsvBuilder<CsvRow>(writer)
                    .build();
            csv.write(Arrays.asList(csvRows));
        }
    }
}


다음은 생성된 csv 파일입니다. 모든 헤더는 대문자로 표시되며 헤더의 순서는 알파벳순으로 정렬됩니다.

"COUNTRY_CODE","ID"
"852","HK"
"1","US"
"81","JP"


그러나 이것이 우리가 기대하는 것입니다.

"id","country_code"
"HK","852"
"US","1"
"JP","81"


해결책



매핑 전략을 사용해야 합니다.
StatefulBeanToCsvBuilder.withMappingStrategyjavadoc에서,

It is perfectly legitimate to read a CSV source, take the mapping strategy from the read operation, and pass it in to this method for a write operation. This conserves some processing time, but, more importantly, preserves header ordering.



즉, 기존 CSV 파일을 읽어 매핑 전략을 초기화해야 합니다. 프로그래밍 방식으로 CSV 파일을 만들고 읽을 수 있습니다.

// Create our strategy
HeaderColumnNameMappingStrategy<CsvRow> strategy = new HeaderColumnNameMappingStrategy<>();
strategy.setType(CsvRow.class);

// Build the header line which respects the declaration order
String headerLine = Arrays.stream(CsvRow.class.getDeclaredFields())
        .map(field -> field.getAnnotation(CsvBindByName.class))
        .filter(Objects::nonNull)
        .map(CsvBindByName::column)
        .collect(Collectors.joining(","));

// Initialize strategy by reading a CSV with header only
try (StringReader reader = new StringReader(headerLine)) {
    CsvToBean<CsvRow> csv = new CsvToBeanBuilder<CsvRow>(reader)
            .withType(CsvRow.class)
            .withMappingStrategy(strategy)
            .build();
    for (CsvRow csvRow : csv) {}
}


이제 전략을 StatefulBeanToCsvBuilder에 전달할 수 있습니다.

StatefulBeanToCsv<CsvRow> csv = new StatefulBeanToCsvBuilder<CsvRow>(writer)
    .withMappingStrategy(strategy)
    .build();


응용 프로그램을 다시 실행하면 예상했던 CSV 파일이 생성됩니다.

"id","country_code"
"HK","852"
"US","1"
"JP","81"


이것이 응용 프로그램의 모습입니다.

public class Application {
    public static void main(String[] args) throws Exception {
        CsvRow[] csvRows = new CsvRow[] {
                new CsvRow("HK", "852"),
                new CsvRow("US", "1"),
                new CsvRow("JP", "81"),
        };

        HeaderColumnNameMappingStrategy<CsvRow> strategy = new HeaderColumnNameMappingStrategy<>();
        strategy.setType(CsvRow.class);

        String headerLine = Arrays.stream(CsvRow.class.getDeclaredFields())
                .map(field -> field.getAnnotation(CsvBindByName.class))
                .filter(Objects::nonNull)
                .map(CsvBindByName::column)
                .collect(Collectors.joining(","));

        try (StringReader reader = new StringReader(headerLine)) {
            CsvToBean<CsvRow> csv = new CsvToBeanBuilder<CsvRow>(reader)
                    .withType(CsvRow.class)
                    .withMappingStrategy(strategy)
                    .build();
            for (CsvRow csvRow : csv) {}
        }

        Path outputPath = Path.of("countries.csv");
        try (var writer = Files.newBufferedWriter(outputPath)) {
            StatefulBeanToCsv<CsvRow> csv = new StatefulBeanToCsvBuilder<CsvRow>(writer)
                    .withMappingStrategy(strategy)
                    .build();
            csv.write(Arrays.asList(csvRows));
        }
    }
}

좋은 웹페이지 즐겨찾기