Java到CSV文件,SuperCSV Dozer:如何避免嵌套对象的循环

我有以下课程..

SurveyResponse.java

import java.util.List; public class SurveyResponse { private int age; private Boolean consentGiven; private List answers; public SurveyResponse() { } public SurveyResponse(final int age, final Boolean consentGiven, final List answers) { this.age = age; this.consentGiven = consentGiven; this.answers = answers; } public int getAge() { return age; } public List getAnswers() { return answers; } public Boolean getConsentGiven() { return consentGiven; } public void setAge(final int age) { this.age = age; } public void setAnswers(final List answers) { this.answers = answers; } public void setConsentGiven(final Boolean consentGiven) { this.consentGiven = consentGiven; } @Override public String toString() { return String.format("SurveyResponse [age=%s, consentGiven=%s, answers=%s]", age, consentGiven, answers); } } 

Answer.java

 public class Answer { private Integer questionNo; private String answer; public Answer() { } public Answer(final Integer questionNo, final String answer) { this.questionNo = questionNo; this.answer = answer; } public String getAnswer() { return answer; } public Integer getQuestionNo() { return questionNo; } public void setAnswer(final String answer) { this.answer = answer; } public void setQuestionNo(final Integer questionNo) { this.questionNo = questionNo; } @Override public String toString() { return String.format("Answer [questionNo=%s, answer=%s]", questionNo, answer); } } 

和我的Main类循环工作正常如下 –

 import java.io.FileWriter; import java.util.Arrays; import java.util.List; import org.supercsv.cellprocessor.FmtBool; import org.supercsv.cellprocessor.Optional; import org.supercsv.cellprocessor.Token; import org.supercsv.cellprocessor.constraint.NotNull; import org.supercsv.cellprocessor.ift.CellProcessor; import org.supercsv.io.dozer.CsvDozerBeanWriter; import org.supercsv.io.dozer.ICsvDozerBeanWriter; import org.supercsv.prefs.CsvPreference; public class Main { private static final String[] FIELD_MAPPING = new String[] { "age", // simple field mapping // (like CsvBeanReader) "consentGiven", // as above "answers[0].questionNo", // indexed (first element) + deep mapping "answers[0].answer", "answers[1].questionNo", // indexed (second element) + deep mapping "answers[1].answer", "answers[2].questionNo", "answers[2].answer" }; /** * @param args * @throws Exception */ public static void main(final String[] args) throws Exception { writeWithDozerCsvBeanWriter(); } private static void writeWithDozerCsvBeanWriter() throws Exception { final CellProcessor[] processors = new CellProcessor[] { new Token(0, null), // age new FmtBool("Y", "N"), // consent new NotNull(), // questionNo 1 new Optional(), // answer 1 new NotNull(), // questionNo 2 new Optional(), // answer 2 new NotNull(), // questionNo 3 new Optional() }; // answer 4 // create the survey responses to write final SurveyResponse response1 = new SurveyResponse(18, true, Arrays.asList(new Answer(1, "Twelve"), new Answer(2, "Albert Einstein"), new Answer(3, "Big Bang Theory"))); final SurveyResponse response2 = new SurveyResponse(0, true, Arrays.asList(new Answer(1, "Thirteen"), new Answer(2, "Nikola Tesla"), new Answer(3, "Stargate"))); final SurveyResponse response3 = new SurveyResponse(42, false, Arrays.asList(new Answer(1, null), new Answer(2, "Carl Sagan"), new Answer(3, "Star Wars"))); final List surveyResponses = Arrays.asList(response1, response2, response3); ICsvDozerBeanWriter beanWriter = null; try { beanWriter = new CsvDozerBeanWriter(new FileWriter("C:\\Users\\Desktop\\Test.csv"), CsvPreference.STANDARD_PREFERENCE); // configure the mapping from the fields to the CSV columns beanWriter.configureBeanMapping(SurveyResponse.class, FIELD_MAPPING); // write the header beanWriter.writeHeader("age", "consentGiven", "questionNo1", "answer1", "questionNo2", "answer2", "questionNo3", "answer3"); // write the beans for (final SurveyResponse surveyResponse : surveyResponses) { beanWriter.write(surveyResponse, processors); } } finally { if (beanWriter != null) { beanWriter.close(); } } } } 

我想避免在字段映射处理器数组中循环,因为在我们的应用程序中,我们有大约100到1000个答案。 所以我写了下面哪个不行。 有人可以解释为什么以下不起作用?

Main2.java

 import java.io.FileWriter; import java.util.Arrays; import java.util.List; import org.supercsv.cellprocessor.FmtBool; import org.supercsv.cellprocessor.Optional; import org.supercsv.cellprocessor.Token; import org.supercsv.cellprocessor.constraint.NotNull; import org.supercsv.cellprocessor.ift.CellProcessor; import org.supercsv.io.dozer.CsvDozerBeanWriter; import org.supercsv.io.dozer.ICsvDozerBeanWriter; import org.supercsv.prefs.CsvPreference; public class Main2 { private static final String[] FIELD_MAPPING = new String[] { "age", "consentGiven", "answers.questionNo", "answers.answer" }; /** * @param args * @throws Exception */ public static void main(final String[] args) throws Exception { writeWithDozerCsvBeanWriter(); } private static void writeWithDozerCsvBeanWriter() throws Exception { final CellProcessor[] processors = new CellProcessor[] { new Token(0, null), // age new FmtBool("Y", "N"), // consent new NotNull(), // questionNo new Optional() // answer }; // create the survey responses to write final SurveyResponse response1 = new SurveyResponse(18, true, Arrays.asList(new Answer(1, "Twelve"), new Answer(2, "Albert Einstein"), new Answer(3, "Big Bang Theory"))); final SurveyResponse response2 = new SurveyResponse(0, true, Arrays.asList(new Answer(1, "Thirteen"), new Answer(2, "Nikola Tesla"), new Answer(3, "Stargate"))); final SurveyResponse response3 = new SurveyResponse(42, false, Arrays.asList(new Answer(1, null), new Answer(2, "Carl Sagan"), new Answer(3, "Star Wars"))); final List surveyResponses = Arrays.asList(response1, response2, response3); ICsvDozerBeanWriter beanWriter = null; try { beanWriter = new CsvDozerBeanWriter(new FileWriter("C:\\Users\\Desktop\\Test.csv"), CsvPreference.STANDARD_PREFERENCE); // configure the mapping from the fields to the CSV columns beanWriter.configureBeanMapping(SurveyResponse.class, FIELD_MAPPING); // write the header beanWriter.writeHeader("age", "consentGiven", "questionNo1", "answer1"); // write the beans for (final SurveyResponse surveyResponse : surveyResponses) { beanWriter.write(surveyResponse, processors); } } finally { if (beanWriter != null) { beanWriter.close(); } } } } 

这是错误

 6 [main] INFO org.dozer.config.GlobalSettings - Trying to find Dozer configuration file: dozer.properties 14 [main] WARN org.dozer.config.GlobalSettings - Dozer configuration file not found: dozer.properties. Using defaults for all Dozer global properties. 15 [main] INFO org.dozer.DozerInitializer - Initializing Dozer. Version: 5.4.0, Thread Name: main 64 [main] INFO org.dozer.jmx.JMXPlatformImpl - Dozer JMX MBean [org.dozer.jmx:type=DozerStatisticsController] auto registered with the Platform MBean Server 65 [main] INFO org.dozer.jmx.JMXPlatformImpl - Dozer JMX MBean [org.dozer.jmx:type=DozerAdminController] auto registered with the Platform MBean Server 68 [main] INFO org.dozer.DozerBeanMapper - Initializing a new instance of dozer bean mapper. 156 [main] ERROR org.dozer.MappingProcessor - Field mapping error --> MapId: null Type: null Source parent class: SurveyResponse Source field name: answers.questionNo Source field type: null Source field value: null Dest parent class: org.supercsv.io.dozer.CsvDozerBeanData Dest field name: columns Dest field type: java.util.List java.lang.IllegalArgumentException: object is not an instance of declaring class at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.dozer.util.ReflectionUtils.invoke(ReflectionUtils.java:323) at org.dozer.propertydescriptor.GetterSetterPropertyDescriptor. getDeepSrcFieldValue(GetterSetterPropertyDescriptor.java:122) at org.dozer.propertydescriptor.GetterSetterPropertyDescriptor. getPropertyValue(GetterSetterPropertyDescriptor.java:75) at org.dozer.fieldmap.FieldMap.getSrcFieldValue(FieldMap.java:84) at org.dozer.MappingProcessor.mapField(MappingProcessor.java:275) at org.dozer.MappingProcessor.map(MappingProcessor.java:248) at org.dozer.MappingProcessor.map(MappingProcessor.java:197) at org.dozer.MappingProcessor.map(MappingProcessor.java:187) at org.dozer.MappingProcessor.map(MappingProcessor.java:133) at org.dozer.MappingProcessor.map(MappingProcessor.java:128) at org.dozer.DozerBeanMapper.map(DozerBeanMapper.java:127) at org.supercsv.io.dozer.CsvDozerBeanWriter.write(CsvDozerBeanWriter.java:132) at MainAvoidIndexing.writeWithDozerCsvBeanWriter(MainAvoidIndexing.java:68) at MainAvoidIndexing.main(MainAvoidIndexing.java:29) Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl. invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.dozer.util.ReflectionUtils.invoke(ReflectionUtils.java:323) at org.dozer.propertydescriptor.GetterSetterPropertyDescriptor. getDeepSrcFieldValue(GetterSetterPropertyDescriptor.java:122) at org.dozer.propertydescriptor.GetterSetterPropertyDescriptor. getPropertyValue(GetterSetterPr opertyDescriptor.java:75) at org.dozer.fieldmap.FieldMap.getSrcFieldValue(FieldMap.java:84) at org.dozer.MappingProcessor.mapField(MappingProcessor.java:275) at org.dozer.MappingProcessor.map(MappingProcessor.java:248) at org.dozer.MappingProcessor.map(MappingProcessor.java:197) at org.dozer.MappingProcessor.map(MappingProcessor.java:187) at org.dozer.MappingProcessor.map(MappingProcessor.java:133) at org.dozer.MappingProcessor.map(MappingProcessor.java:128) at org.dozer.DozerBeanMapper.map(DozerBeanMapper.java:127) at org.supercsv.io.dozer.CsvDozerBeanWriter.write(CsvDozerBeanWriter.java:132) at MainAvoidIndexing.writeWithDozerCsvBeanWriter(MainAvoidIndexing.java:68) at MainAvoidIndexing.main(MainAvoidIndexing.java:29) 

SurveyResponseanswers字段是List ,因此您必须使用索引映射来配置CsvDozerBeanWriter 。 索引映射实际上并不是循环的 – 它只是Dozer如何知道要填充List的哪个元素。 如果要忽略列,这非常重要 – 它实际上会为该列插入一个null元素。

如果您不使用索引映射(并且只使用"answers" ),那么您需要配置一个可以获取List并将其转换为可用作CSV的内容的单元处理器。

动态配置字段映射

(不是你问的结果,但我会留下以备将来参考)

并不意味着您必须使用静态String数组配置字段映射。 您可以使用简单的for循环动态填充数组。 例如,

  String[] fieldMapping = new String[10]; fieldMapping[0] = "age"; fieldMapping[1] = "consentGiven"; int answerStartIndex = 2; int answerEndIndex = 8; for (int i = answerStartIndex; i <= answerEndIndex; i++){ fieldMapping[i] = String.format("answers[%s].questionNo", i - answerStartIndex); } fieldMapping[9] = "age"; 

这会给你一个fieldMapping:

 [age, consentGiven, answers[0].questionNo, answers[1].questionNo, answers[2].questionNo, answers[3].questionNo, answers[4].questionNo, answers[5].questionNo, answers[6].questionNo, age]