Подтвердить что ты не робот

OpenCSV - Как сопоставить выбранные столбцы с Java Bean независимо от порядка?

У меня есть файл CSV со следующими столбцами: id, fname, telephone, lname, address.

У меня есть класс Person с членами id, fname и lname. Я хочу сопоставить только эти столбцы с объектом Person из файла CSV и отбросить столбцы telephone и address. Как я могу это сделать? Решение должно масштабироваться по мере добавления большего количества столбцов в будущем. И должен работать независимо от положения столбца.

В идеальном решении пользователь будет указывать только столбцы для чтения, и он должен просто работать.

4b9b3361

Ответ 1

Вы можете использовать HeaderColumnNameTranslateMappingStrategy. Давайте предположим, что для упрощения CSV имеет следующие столбцы: Id, Fname, Telephone, Lname, Address.

CsvToBean<Person> csvToBean = new CsvToBean<Person>();

Map<String, String> columnMapping = new HashMap<String, String>();
columnMapping.put("Id", "id");
columnMapping.put("Fname", "fname");
columnMapping.put("Lname", "lname");

HeaderColumnNameTranslateMappingStrategy<Person> strategy = new HeaderColumnNameTranslateMappingStrategy<Person>();
strategy.setType(Person.class);
strategy.setColumnMapping(columnMapping);

List<Person> list = null;
CSVReader reader = new CSVReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("test.csv")));
list = csvToBean.parse(strategy, reader);

Сопоставление столбцов будет отображать столбцы с вашим объектом Person.

Ответ 2

Последние версии OpenCSV обесценивают метод parse(X, Y), и он снова начал использовать BeanBuilder, поэтому верхний ответ устарел.

try {
    CsvToBeanBuilder<PersonCSV> beanBuilder = new CsvToBeanBuilder<>(new InputStreamReader(new FileInputStream("your.csv")));

    beanBuilder.withType(PersonCSV.class);
    // build methods returns a list of Beans
    beanBuilder.build().parse().forEach(e -> log.error(e.toString()));

} catch (FileNotFoundException e) {
    log.error(e.getMessage(), e);
}

Эти методы позволяют очистить код и удалить MappingStrategy (вы все равно можете использовать его, если хотите спагетти), поэтому вы можете аннотировать свой CSV-класс следующим образом:

@CsvDate("dd/MM/yyyy hh:mm:ss")
@CsvBindByName(column = "Time Born", required = true)
private Date birthDate;

Ответ 3

Я не могу говорить за opencsv, но это легко достижимо с помощью Super CSV, который имеет два разных readers, которые поддерживают частичное чтение (игнорируя столбцы), а также чтение в джавабе. CsvDozerBeanReader даже способен глубокое и индексированное сопоставление, поэтому вы можете сопоставить вложенные поля.

Мы (команда Super CSV) только что выпустили версию 2.0.0, которая доступна в Maven central или SourceForge.

Обновление

Вот пример (на основе теста в проекте GitHub, который вы создали), который использует Super CSV вместо opencsv. Обратите внимание, что для предпочтений CSV необходим флаг surroundingSpacesNeedQuotes, поскольку ваш пример CSV файла недопустим (он имеет пробелы между полями - пробелы считаются частью данных в CSV).

ICsvBeanReader beanReader = null;
try {
    beanReader = new CsvBeanReader(
            new InputStreamReader(
                    ClassLoader.getSystemResourceAsStream("test.csv")),
            new CsvPreference.Builder(CsvPreference.STANDARD_PREFERENCE)
                    .surroundingSpacesNeedQuotes(true).build());

    List<String> columnsToMap = Arrays.asList("fname", "telephone", "id");

    // read the CSV header (and set any unwanted columns to null)
    String[] header = beanReader.getHeader(true);
    for (int i = 0; i < header.length; i++) {
        if (!columnsToMap.contains(header[i])) {
            header[i] = null;
        }
    }

    Person person;
    while ((person = beanReader.read(Person.class, header)) != null) {
        System.out.println(person);
    }

} finally {
    beanReader.close();
}

Ответ 4

Используйте uniVocity-parsers и покончите с этим. Неважно, как организованы столбцы во входном CSV, будут проанализированы только те, которые вам нужны.

При записи столбцы, которые у вас есть в классе, будут записаны в соответствующие столбцы, а остальные будут пустыми.

Здесь класс с некоторыми примерами:

class TestBean {

    // if the value parsed in the quantity column is "?" or "-", it will be replaced by null.
    @NullString(nulls = { "?", "-" })
    // if a value resolves to null, it will be converted to the String "0".
    @Parsed(defaultNullRead = "0")
    private Integer quantity;   // The attribute type defines which conversion will be executed when processing the value.

    @Trim
    @LowerCase
    // the value for the comments attribute is in the column at index 4 (0 is the first column, so this means fifth column in the file)
    @Parsed(index = 4)
    private String comments;

    // you can also explicitly give the name of a column in the file.
    @Parsed(field = "amount")
    private BigDecimal amount;

    @Trim
    @LowerCase
    // values "no", "n" and "null" will be converted to false; values "yes" and "y" will be converted to true
    @BooleanString(falseStrings = { "no", "n", "null" }, trueStrings = { "yes", "y" })
    @Parsed
    private Boolean pending;
}

Здесь, как получить список TestBean

BeanListProcessor<TestBean> rowProcessor = new BeanListProcessor<TestBean>(TestBean.class);

CsvParserSettings parserSettings = new CsvParserSettings();
parserSettings.setRowProcessor(rowProcessor);
parserSettings.setHeaderExtractionEnabled(true);

CsvParser parser = new CsvParser(parserSettings);
parser.parse(getReader("/examples/bean_test.csv"));

List<TestBean> beans = rowProcessor.getBeans();

Раскрытие информации: Я являюсь автором этой библиотеки. Это с открытым исходным кодом и бесплатно (лицензия Apache V2.0).

Ответ 5

Вот хороший способ использовать OpenCSV для преобразования в POJO в целом:

protected <T> List<T> mapToCSV(String csvContent, Class<T> mapToClass) {
    CsvToBean<T> csvToBean = new CsvToBean<T>();

    Map<String, String> columnMapping = new HashMap<>();
    Arrays.stream(mapToClass.getDeclaredFields()).forEach(field -> {
        columnMapping.put(field.getName(), field.getName()); 
    });

    HeaderColumnNameTranslateMappingStrategy<T> strategy = new HeaderColumnNameTranslateMappingStrategy<T>();
    strategy.setType(mapToClass);
    strategy.setColumnMapping(columnMapping);

    CSVReader reader = new CSVReader(new StringReader(csvContent));
    return csvToBean.parse(strategy, reader);
}


public static class MyPojo {
    private String foo, bar;

    public void setFoo(String foo) {
        this.foo = foo;
    }

    public void setBar(String bar) {
        this.bar = bar;
    }
}

Затем из вашего теста вы можете использовать:

List<MyPojo> list = mapToCSV(csvContent, MyPojo.class);

Ответ 6

Я реализовал гибкое решение для решения этой проблемы. Он очень прост в использовании и код с примером доступен на моем github ниже:

https://github.com/jsinghfoss/opencsv

Ответ 7

SimpleFlatMapper может сделать это легко см. Начало работы csv, используя заголовки csv или указав вручную, какие столбцы соответствуют какому свойству.

CsvParser
    .mapTo(MyObject.class)
    .forEach(file, System.out::println);

Ответ 8

Посмотрите на jcsvdao, https://github.com/eric-mckinley/jcsvdao/, использует файлы сопоставления стиля hibernate и может обрабатывать отношения 1to1 и 1toMany. Хорошо, если вы не владеете файлами csv, так как имеют гибкие стратегии соответствия.

Ответ 9

Использование примера jcvsdao

Пример файла CSV пользователя

Username, Email, Registration Date, Age, Premium User
Jimmy, [email protected], 04-05-2016, 15, Yes, M
Bob, [email protected], 15-01-2012, 32, No, M
Alice, [email protected], 22-09-2011, 24, No, F
Mike, [email protected], 11-03-2012, 18, Yes, M
Helen, [email protected], 02-12-2013, 22, Yes, F
Tom, [email protected], 08-11-2015, 45, No, M

Создать CsvDao

CSVDaoFactory factory = new CSVDaoFactory("/csv-config.xml");
CSVDao dao = new CSVDao(factory);
List<UserDetail> users = dao.find(UserDetail.class);

CSV-config.xml

<CSVConfig>
    <mappingFiles fileType="resource">
        <mappingFile>/example01/mapping/UserDetail.csv.xml</mappingFile>
    </mappingFiles>
</CSVConfig>

UserDetail.csv.xml

<CSVMapping className="org.jcsvdao.examples.example01.model.UserDetail" csvFile="csv-examples/example01/users.txt" delimiter="," ignoreFirstLine="true">
    <matchAll/>
    <properties>
        <property index="0" property="username" primaryKey="true"/>
        <property index="1" property="email"/>
        <property index="2" property="registrationDate" converter="myDateConverter"/>
        <property index="3" property="age"/>
        <property index="4" property="premiumUser" converter="yesNoConverter"/>
        <property index="5" property="gender" converter="myGenderConverter"/>
    </properties>
    <converters>
        <dateConverter converterName="myDateConverter" format="dd-MM-yyyy"/>
        <booleanConverter converterName="yesNoConverter" positive="Yes" negative="No"/>
        <customConverter converterName="myGenderConverter" converterClass="org.jcsvdao.examples.example01.converter.GenderCustomerConverter"/>
    </converters>
</CSVMapping>