Файл Java в CSV, SuperCSV Dozer: как избежать зацикливания вложенного объекта
У меня есть следующие классы..
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);
}
}
и мой основной класс с циклом, который работает нормально, как показано ниже -
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();
}
}
}
}
Я хочу избежать зацикливания в массивах отображения полей и процессоров, потому что... в нашем приложении у нас есть от 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<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();
}
}
}
}
Вот ошибка
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 ответ
answers
поле в SurveyResponse
это List
поэтому вы должны использовать индексированное отображение для настройки CsvDozerBeanWriter
, Индексированное отображение на самом деле не зацикливается - это лишь то, как Dozer знает, какой элемент списка нужно заполнить. Это очень важно, если бы вы игнорировали столбец - он фактически вставил бы null
элемент для этого столбца.
Если вы не используете индексированное отображение (и просто использовать "answers"
) тогда вам нужно настроить процессор ячейки, который может принимать List<Answer>
и преобразовать его во что-то, что можно использовать как 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";
Что даст вам отображение поля:
[age, consentGiven, answers[0].questionNo, answers[1].questionNo,
answers[2].questionNo, answers[3].questionNo, answers[4].questionNo,
answers[5].questionNo, answers[6].questionNo, age]