问题
I have the below classes..
SurveyResponse.java
import java.util.List;
public class SurveyResponse {
private int age;
private Boolean consentGiven;
private List<Answer> answers;
public SurveyResponse() {
}
public SurveyResponse(final int age, final Boolean consentGiven, final List<Answer>
answers) {
this.age = age;
this.consentGiven = consentGiven;
this.answers = answers;
}
public int getAge() {
return age;
}
public List<Answer> getAnswers() {
return answers;
}
public Boolean getConsentGiven() {
return consentGiven;
}
public void setAge(final int age) {
this.age = age;
}
public void setAnswers(final List<Answer> 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);
}
}
and my Main class with the looping which works fine as below -
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<SurveyResponse> 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();
}
}
}
}
I want to avoid the looping in the field mapping and processors arrays because..in our app, we have around 100 to 1000 answers. so I wrote the below which is not working. Could someone shed some light as why the below is not working?
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<SurveyResponse> 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();
}
}
}
}
Here is the error
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)
回答1:
The answers
field in SurveyResponse
is a List
, so you have to use indexed mapping to configure CsvDozerBeanWriter
. Indexed mapping is not actually looping - it's just how Dozer knows which element of a List to populate. This is extremely important if you were to ignore a column - it would actually insert a null
element for that column.
If you don't use indexed mapping (and just use "answers"
) then you'd need to configure a cell processor that can take a List<Answer>
and convert it to something that can be used as CSV.
Dynamically configuring the field mapping
(Not what you were asking as it turns out, but I'll leave for future reference)
That doesn't mean you have to configure the field mapping with a static String array. You can populate the array dynamically using a simple for-loop. For example,
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";
Which will give you a fieldMapping of:
[age, consentGiven, answers[0].questionNo, answers[1].questionNo,
answers[2].questionNo, answers[3].questionNo, answers[4].questionNo,
answers[5].questionNo, answers[6].questionNo, age]
来源:https://stackoverflow.com/questions/24395851/java-to-csv-file-supercsv-dozer-how-to-avoid-looping-for-a-nested-object