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

时间:2014-06-24 20:39:50

标签: java csv dozer supercsv

我有以下课程..

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);
}

}

和我的 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<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 个答案:

答案 0 :(得分:0)

answers中的SurveyResponse字段是List,因此您必须使用索引映射来配置CsvDozerBeanWriter。索引映射实际上并不是循环的 - 它只是Dozer如何知道要填充List的哪个元素。如果要忽略列,这非常重要 - 它实际上会为该列插入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";

这会给你一个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]