问题
I try to map a nested bean structure with openCSV. I found the @CsvRecurse annotation, but this does not seem to work if a nested bean is used multiple times.
What options do I have to solve this?
Example (adapted from the docs linked above)
Data structure to map :
title,author1 given name,author1 surname,author2 given name,author2 surname
Space Opera 2.0,Andrew,Jones,Hanna,Smith
I would like to get the following beans
public class Book {
@CsvBindByName
private String title;
// TODO: How to bind author1 and author2?
private Author author1;
private Author author2;
// Accessor methods go here.
}
public class Author {
// TODO: can I somehow use a prefix/Regex for the column here to differentiate between author1 and author2?
@CsvBindByName(column = "author[1/2] given name")
private String givenName;
@CsvBindByName(column = "author[1/2] surname")
private String surname;
// Accessor methods go here.
}
回答1:
You can use custom 'ColumnPositionMappingStrategy'. Override 'populateNewBean' method which takes String[] (row of csv) and can form any object out of it.
Like this
'@CsvRecurse' should work for Open OpenCSV 5.0 btw.
回答2:
Here is another option:
The opencsv library has a lot of useful and flexible annotations, but in this specific case, I would not use any of them.
Instead, I would use the opencsv CSVReaderHeaderAware class. Using this will allow you to keep your two Book
and Author
classes. The only thing I would change is to add constructors to each class as follows:
Book:
public class Book {
private final String title;
private final Author author1;
private final Author author2;
public Book(String title, Author author1, Author author2) {
this.title = title;
this.author1 = author1;
this.author2 = author2;
}
public String getTitle() {
return title;
}
public Author getAuthor1() {
return author1;
}
public Author getAuthor2() {
return author2;
}
}
Author:
public class Author {
private final String givenName;
private final String surname;
public Author(String givenName, String surname) {
this.givenName = givenName;
this.surname = surname;
}
public String getGivenName() {
return givenName;
}
public String getSurname() {
return surname;
}
}
To populate a list of Book
objects from a CSV file, do this:
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import com.opencsv.CSVReaderHeaderAware;
import com.opencsv.exceptions.CsvValidationException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
...
public static void main(String[] args) throws FileNotFoundException,
IOException, CsvValidationException {
String bookFile = "/path/to/titles.csv";
CSVReaderHeaderAware csvReader = new CSVReaderHeaderAware(new FileReader(bookFile));
List<Book> books = new ArrayList();
Map<String, String> bookRecord;
while ((bookRecord = csvReader.readMap()) != null) {
books.add(handleBook(bookRecord));
}
}
private static Book handleBook(Map<String, String> bookRecord) {
Author author1 = new Author(
bookRecord.get("author1 given name"),
bookRecord.get("author1 surname")
);
Author author2 = new Author(
bookRecord.get("author2 given name"),
bookRecord.get("author2 surname")
);
return new Book(bookRecord.get("title"), author1, author2);
}
Each row of data from the CSV file is read into a Map
object, where the file headers are the map's keys (you need make sure the file headers are unique). The Map's corresponding values represent one row of data from the CSV file.
The only downside to this is you may need to cast string values to other data types - although not in this case, because the data items are already strings.
来源:https://stackoverflow.com/questions/62281443/opencsv-with-complex-bean-structure