CSVファイルのすべての列が指定されたMappingsBeanクラスを作成しました。次に、XMLファイルを解析し、マッピングBeanのリストを作成します。次に、そのデータをレポートとしてCSVファイルに書き込みます。
私は次の注釈を使用しています:
_public class MappingsBean {
@CsvBindByName(column = "TradeID")
@CsvBindByPosition(position = 0)
private String tradeId;
@CsvBindByName(column = "GWML GUID", required = true)
@CsvBindByPosition(position = 1)
private String gwmlGUID;
@CsvBindByName(column = "MXML GUID", required = true)
@CsvBindByPosition(position = 2)
private String mxmlGUID;
@CsvBindByName(column = "GWML File")
@CsvBindByPosition(position = 3)
private String gwmlFile;
@CsvBindByName(column = "MxML File")
@CsvBindByPosition(position = 4)
private String mxmlFile;
@CsvBindByName(column = "MxML Counterparty")
@CsvBindByPosition(position = 5)
private String mxmlCounterParty;
@CsvBindByName(column = "GWML Counterparty")
@CsvBindByPosition(position = 6)
private String gwmlCounterParty;
}
_
そして、StatefulBeanToCsv
クラスを使用してCSVファイルに書き込みます。
_File reportFile = new File(reportOutputDir + "/" + REPORT_FILENAME);
Writer writer = new PrintWriter(reportFile);
StatefulBeanToCsv<MappingsBean> beanToCsv = new
StatefulBeanToCsvBuilder(writer).build();
beanToCsv.write(makeFinalMappingBeanList());
writer.close();
_
このアプローチの問題は、@CsvBindByPosition(position = 0)
を使用して位置を制御すると、列名を生成できないことです。 @CsvBindByName(column = "TradeID")
を使用すると、列の位置を設定できません。
両方の注釈を使用して、列ヘッダー付きのCSVファイルを作成し、列の位置を制御できる方法はありますか?
よろしく、Vikram Pathania
同様の問題が発生しました。私の知る限り、OpenCSVには、カスタム列名and順序付けでCSVにBeanを書き込むことを可能にする組み込み機能はありません。
OpenCSVですぐに使用できる2つの主なMappingStrategy
iesがあります。
HeaderColumnNameMappingStrategy
:カスタム名に基づいてCVSファイルの列をBeanフィールドにマッピングできます。 BeanをCSVに書き込むと、列ヘッダー名を変更できますが、列の順序を制御できませんColumnPositionMappingStrategy
:列の順序に基づいてCSVファイルの列をBeanフィールドにマッピングできます。 BeanをCSVに書き込むとき、列の順序を制御できますが、空のヘッダーを取得します(実装はヘッダーとして_new String[0]
_を返します)カスタム列名と順序の両方を実現するために私が見つけた唯一の方法は、カスタムMappingStrategy
を書くことです。
カスタムMappingStrategy
を作成します。
_class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
private static final String[] HEADER = new String[]{"TradeID", "GWML GUID", "MXML GUID", "GWML File", "MxML File", "MxML Counterparty", "GWML Counterparty"};
@Override
public String[] generateHeader() {
return HEADER;
}
}
_
StatefulBeanToCsvBuilder
で使用します:
_final CustomMappingStrategy<MappingsBean> mappingStrategy = new CustomMappingStrategy<>();
mappingStrategy.setType(MappingsBean.class);
final StatefulBeanToCsv<MappingsBean> beanToCsv = new StatefulBeanToCsvBuilder<MappingsBean>(writer)
.withMappingStrategy(mappingStrategy)
.build();
beanToCsv.write(makeFinalMappingBeanList());
writer.close()
_
MappingsBean
クラスでは、CsvBindByPosition
注釈を残しました-順序を制御します(このソリューションではCsvBindByName
注釈は不要です)。カスタムマッピング戦略のおかげで、結果のCSVファイルにヘッダー列名が含まれます。
このソリューションの欠点は、CsvBindByPosition
アノテーションを使用して列の順序を変更する場合、カスタムマッピング戦略でHEADER
定数も手動で変更する必要があることです。
最初の解決策は機能しますが、私にとっては良くありませんでした。 MappingStrategy
の組み込み実装に基づいて、さらに別の実装を思いつきました。
_class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
@Override
public String[] generateHeader() {
final int numColumns = findMaxFieldIndex();
if (!isAnnotationDriven() || numColumns == -1) {
return super.generateHeader();
}
header = new String[numColumns + 1];
BeanField beanField;
for (int i = 0; i <= numColumns; i++) {
beanField = findField(i);
String columnHeaderName = extractHeaderName(beanField);
header[i] = columnHeaderName;
}
return header;
}
private String extractHeaderName(final BeanField beanField) {
if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) {
return StringUtils.EMPTY;
}
final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0];
return bindByNameAnnotation.column();
}
}
_
このカスタム戦略は、最初のソリューションとまったく同じStatefulBeanToCsvBuilder
で使用できます(mappingStrategy.setType(MappingsBean.class);
を呼び出すことを忘れないでください。そうしないと、このソリューションは機能しません)。
現在、MappingsBean
にはCsvBindByName
とCsvBindByPosition
の両方の注釈を含める必要があります。 1つ目はヘッダー列名を指定し、2つ目は出力CSVヘッダーの列の順序を作成します。ここで(注釈を使用して)列名またはMappingsBean
クラスの順序を変更すると、その変更は出力CSVファイルに反映されます。
上記の回答を新しいバージョンに合わせて修正しました。
package csvpojo;
import org.Apache.commons.lang3.StringUtils;
import com.opencsv.bean.BeanField;
import com.opencsv.bean.ColumnPositionMappingStrategy;
import com.opencsv.bean.CsvBindByName;
import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
@Override
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
super.setColumnMapping(new String[ FieldUtils.getAllFields(bean.getClass()).length]);
final int numColumns = findMaxFieldIndex();
if (!isAnnotationDriven() || numColumns == -1) {
return super.generateHeader(bean);
}
String[] header = new String[numColumns + 1];
BeanField<T> beanField;
for (int i = 0; i <= numColumns; i++) {
beanField = findField(i);
String columnHeaderName = extractHeaderName(beanField);
header[i] = columnHeaderName;
}
return header;
}
private String extractHeaderName(final BeanField<T> beanField) {
if (beanField == null || beanField.getField() == null
|| beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) {
return StringUtils.EMPTY;
}
final CsvBindByName bindByNameAnnotation = beanField.getField()
.getDeclaredAnnotationsByType(CsvBindByName.class)[0];
return bindByNameAnnotation.column();
}
}
次に、これを呼び出してCSVを生成します。 POJOとして訪問者を使用して、必要に応じてデータを入力、更新しました。
CustomMappingStrategy<Visitors> mappingStrategy = new CustomMappingStrategy<>();
mappingStrategy.setType(Visitors.class);
// writing sample
List<Visitors> beans2 = new ArrayList<Visitors>();
Visitors v = new Visitors();
v.set_1_firstName(" test1");
v.set_2_lastName("lastname1");
v.set_3_visitsToWebsite("876");
beans2.add(v);
v = new Visitors();
v.set_1_firstName(" firstsample2");
v.set_2_lastName("lastname2");
v.set_3_visitsToWebsite("777");
beans2.add(v);
Writer writer = new FileWriter("G://output.csv");
StatefulBeanToCsv<Visitors> beanToCsv = new StatefulBeanToCsvBuilder<Visitors>(writer)
.withMappingStrategy(mappingStrategy).withSeparator(',').withApplyQuotesToAll(false).build();
beanToCsv.write(beans2);
writer.close();
私のBeanアノテーションは次のようになります
@CsvBindByName (column = "First Name", required = true)
@CsvBindByPosition(position=1)
private String firstName;
@CsvBindByName (column = "Last Name", required = true)
@CsvBindByPosition(position=0)
private String lastName;
トピックスターターと同じ結果を達成したかったが、生成されたCSVをPOJOにインポートできるようにしたかった。それを達成するのに役立つソリューションはありませんでした。
結果を得るには、@ CsvBindByPositionの使用を拒否する必要がありました。この場合-ColumnPositionMappingStrategyが自動的に選択されたためです。ドキュメントごと: この戦略では、ファイルにヘッダーがないことが必要です 。
ただし、列名によるバインディングを使用してopenCSVで記述した場合、ヘッダーが自動的に追加されます(ヘッダーは削除できますが、便利です)。
HeaderColumnNameMappingStrategy
mappingStrategy.setColumnOrderOnWrite(Comparator<String> writeOrder)
csvUtilsによるcsvの読み取り/書き込み
import com.opencsv.CSVWriter;
import com.opencsv.bean.*;
import org.springframework.web.multipart.MultipartFile;
import Java.io.*;
import Java.util.List;
public class CsvUtils {
private CsvUtils() {
}
public static <T> String convertToCsv(List<T> entitiesList, MappingStrategy<T> mappingStrategy) throws Exception {
try (Writer writer = new StringWriter()) {
StatefulBeanToCsv<T> beanToCsv = new StatefulBeanToCsvBuilder<T>(writer)
.withMappingStrategy(mappingStrategy)
.withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
.build();
beanToCsv.write(entitiesList);
return writer.toString();
}
}
@SuppressWarnings("unchecked")
public static <T> List<T> convertFromCsv(MultipartFile file, Class clazz) throws IOException {
try (Reader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) {
CsvToBean<T> csvToBean = new CsvToBeanBuilder<T>(reader).withType(clazz).build();
return csvToBean.parse();
}
}
}
インポート/エクスポートのPOJO
public class LocalBusinessTrainingPairDTO {
//this is used for CSV columns ordering on exporting LocalBusinessTrainingPairs
public static final String[] FIELDS_ORDER = {"leftId", "leftName", "rightId", "rightName"};
@CsvBindByName(column = "leftId")
private int leftId;
@CsvBindByName(column = "leftName")
private String leftName;
@CsvBindByName(column = "rightId")
private int rightId;
@CsvBindByName(column = "rightName")
private String rightName;
// getters/setters omitted, do not forget to add them
}
事前定義された文字列順序のカスタムコンパレータ:
public class OrderedComparatorIgnoringCase implements Comparator<String> {
private List<String> predefinedOrder;
public OrderedComparatorIgnoringCase(String[] predefinedOrder) {
this.predefinedOrder = new ArrayList<>();
for (String item : predefinedOrder) {
this.predefinedOrder.add(item.toLowerCase());
}
}
@Override
public int compare(String o1, String o2) {
return predefinedOrder.indexOf(o1.toLowerCase()) - predefinedOrder.indexOf(o2.toLowerCase());
}
}
POJOの書き順(最初の質問への回答)
public static void main(String[] args) throws Exception {
List<LocalBusinessTrainingPairDTO> localBusinessTrainingPairsDTO = new ArrayList<>();
LocalBusinessTrainingPairDTO localBusinessTrainingPairDTO = new LocalBusinessTrainingPairDTO();
localBusinessTrainingPairDTO.setLeftId(1);
localBusinessTrainingPairDTO.setLeftName("leftName");
localBusinessTrainingPairDTO.setRightId(2);
localBusinessTrainingPairDTO.setRightName("rightName");
localBusinessTrainingPairsDTO.add(localBusinessTrainingPairDTO);
//Creating HeaderColumnNameMappingStrategy
HeaderColumnNameMappingStrategy<LocalBusinessTrainingPairDTO> mappingStrategy = new HeaderColumnNameMappingStrategy<>();
mappingStrategy.setType(LocalBusinessTrainingPairDTO.class);
//Setting predefined order using String comparator
mappingStrategy.setColumnOrderOnWrite(new OrderedComparatorIgnoringCase(LocalBusinessTrainingPairDTO.FIELDS_ORDER));
String csv = convertToCsv(localBusinessTrainingPairsDTO, mappingStrategy);
System.out.println(csv);
}
エクスポートされたCSVをPOJOに読み込む(元の回答に追加)
重要:名前によるバインディングを引き続き使用しているため、CSVの順序は変更できません:
public static void main(String[] args) throws Exception {
//omitted code from writing
String csv = convertToCsv(localBusinessTrainingPairsDTO, mappingStrategy);
//Exported CSV should be compatible for further import
File temp = File.createTempFile("tempTrainingPairs", ".csv");
temp.deleteOnExit();
BufferedWriter bw = new BufferedWriter(new FileWriter(temp));
bw.write(csv);
bw.close();
MultipartFile multipartFile = new MockMultipartFile("tempTrainingPairs.csv", new FileInputStream(temp));
List<LocalBusinessTrainingPairDTO> localBusinessTrainingPairDTOList = convertFromCsv(multipartFile, LocalBusinessTrainingPairDTO.class);
}
このスレッドのおかげで、本当に役に立ちました...いくつかのフィールドに注釈が付けられていない(読み取り/書き込み用ではない)POJOも受け入れるために、提供されているソリューションを少し強化しました。
public class ColumnAndNameMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
@Override
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
super.setColumnMapping(new String[ getAnnotatedFields(bean)]);
final int numColumns = getAnnotatedFields(bean);
final int totalFieldNum = findMaxFieldIndex();
if (!isAnnotationDriven() || numColumns == -1) {
return super.generateHeader(bean);
}
String[] header = new String[numColumns];
BeanField<T> beanField;
for (int i = 0; i <= totalFieldNum; i++) {
beanField = findField(i);
if (isFieldAnnotated(beanField.getField())) {
String columnHeaderName = extractHeaderName(beanField);
header[i] = columnHeaderName;
}
}
return header;
}
private int getAnnotatedFields(T bean) {
return (int) Arrays.stream(FieldUtils.getAllFields(bean.getClass()))
.filter(this::isFieldAnnotated)
.count();
}
private boolean isFieldAnnotated(Field f) {
return f.isAnnotationPresent(CsvBindByName.class) || f.isAnnotationPresent(CsvCustomBindByName.class);
}
private String extractHeaderName(final BeanField beanField) {
if (beanField == null || beanField.getField() == null) {
return StringUtils.EMPTY;
}
Field field = beanField.getField();
if (field.getDeclaredAnnotationsByType(CsvBindByName.class).length != 0) {
final CsvBindByName bindByNameAnnotation = field.getDeclaredAnnotationsByType(CsvBindByName.class)[0];
return bindByNameAnnotation.column();
}
if (field.getDeclaredAnnotationsByType(CsvCustomBindByName.class).length != 0) {
final CsvCustomBindByName bindByNameAnnotation = field.getDeclaredAnnotationsByType(CsvCustomBindByName.class)[0];
return bindByNameAnnotation.column();
}
return StringUtils.EMPTY;
}
}
モデルクラス(この例ではCsvRow
行)に表示されるメンバー変数の順序に基づいてCSV列を並べ替えるだけの場合は、Comparator
実装を使用してこれをかなり簡単な方法で解決します。 Kotlinでこれを行う例を次に示します。
class ByMemberOrderCsvComparator : Comparator<String> {
private val memberOrder by lazy {
FieldUtils.getAllFields(CsvRow::class.Java)
.map { it.getDeclaredAnnotation(CsvBindByName::class.Java) }
.map { it?.column ?: "" }
.map { it.toUpperCase(Locale.US) } // OpenCSV UpperCases all headers, so we do this to match
}
override fun compare(field1: String?, field2: String?): Int {
return memberOrder.indexOf(field1) - memberOrder.indexOf(field2)
}
}
このComparator
は次のことを行います。
CsvRow
)@CsvBindByName
アノテーションを持つすべてのものを(CsvRow
モデルで指定した順序で)検索します次に、このComparator
をMappingStrategy
に適用すると、指定された順序に基づいてソートされます。
val mappingStrategy = HeaderColumnNameMappingStrategy<OrderSummaryCsvRow>()
mappingStrategy.setColumnOrderOnWrite(ByMemberOrderCsvComparator())
mappingStrategy.type = CsvRow::class.Java
mappingStrategy.setErrorLocale(Locale.US)
val csvWriter = StatefulBeanToCsvBuilder<OrderSummaryCsvRow>(writer)
.withMappingStrategy(mappingStrategy)
.build()
参考のために、以下にCsvRow
クラスの例を示します(これを必要に応じて独自のモデルに置き換えます)。
data class CsvRow(
@CsvBindByName(column = "Column 1")
val column1: String,
@CsvBindByName(column = "Column 2")
val column2: String,
@CsvBindByName(column = "Column 3")
val column3: String,
// Other columns here ...
)
次のようにCSVが生成されます。
"COLUMN 1","COLUMN 2","COLUMN 3",...
"value 1a","value 2a","value 3a",...
"value 1b","value 2b","value 3b",...
このアプローチの利点は、列名をハードコーディングする必要がなくなることです。これにより、列を追加/削除する必要がある場合に物事が大幅に簡素化されます。
以下のようなものを試してください:
private static class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
String[] header;
public CustomMappingStrategy(String[] cols) {
header = cols;
}
@Override
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
return header;
}
}
次に、次のように使用します。
String[] columns = new String[]{"Name", "Age", "Company", "Salary"};
CustomMappingStrategy<Employee> mappingStrategy = new CustomMappingStrategy<Employee>(columns);
列はBeanの列であり、従業員はBeanです
GetDeclaredAnnotationsByTypeメソッドはないが、元のフィールド名が必要な場合:
beanField.getField()。getName()
public class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
@Override
public String[] generateHeader() {
final int numColumns = findMaxFieldIndex();
if (!isAnnotationDriven() || numColumns == -1) {
return super.generateHeader();
}
header = new String[numColumns + 1];
BeanField beanField;
for (int i = 0; i <= numColumns; i++) {
beanField = findField(i);
String columnHeaderName = extractHeaderName(beanField);
header[i] = columnHeaderName;
}
return header;
}
private String extractHeaderName(final BeanField beanField) {
if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotations().length == 0) {
return StringUtils.EMPTY;
}
return beanField.getField().getName();
}
}