将通用POJO写入CSV变换器

时间:2017-01-06 16:57:51

标签: java csv generics amazon-kinesis-firehose jackson-dataformat-csv

我的用例是编写一个通用的CSV转换器,它应该能够将任何Java POJO转换为CSV字符串。

我的实施:

public <T> List<String> convertToString(List<T> objectList) {

        List<String> stringList = new ArrayList<>();
        char delimiter = ',';
        char quote = '"';
        String lineSep = "\n";

        CsvMapper mapper = new CsvMapper();
        CsvSchema schema = mapper.schemaFor(!HOW_TO!);

        for (T object : objectList) {

            try {
                String csv = mapper.writer(schema
                        .withColumnSeparator(delimiter)
                        .withQuoteChar(quote)
                        .withLineSeparator(lineSep)).writeValueAsString(object);

            } catch (JsonProcessingException e) {

                System.out.println(e);
            }
        }

        return stringList;
}

我使用的是Jackson-dataformat-csv库,但我坚持了!HOW_TO! part,即如何从objectList中提取对象的.class。我正在学习并遇到了类型擦除,所以我认为除了将.class作为参数添加到我的函数之外,它是不可能的。但是我也是使用Java Reflection从通用实体中提取这个对象列表,所以我不能选择提供.class参数。

有解决方法吗?

OR

我可以转换具有添加分隔符,引号字符,行分隔符等功能的通用List<T> objectList to List<String> csvList的任何其他方法/库。

谢谢!

3 个答案:

答案 0 :(得分:1)

有一个简单的选择。我在代码中添加了一些行来显示它:

public <T> List<String> convertToString(List<T> objectList) {

    if(objectList.isEmpty())
        return Collections.emptyList();

    T entry = objectList.get(0);

    List<String> stringList = new ArrayList<>();
    char delimiter = ',';
    char quote = '"';
    String lineSep = "\n";

    CsvMapper mapper = new CsvMapper();
    CsvSchema schema = mapper.schemaFor(entry.getClass());

    for (T object : objectList) {

        try {
            String csv = mapper.writer(schema
                    .withColumnSeparator(delimiter)
                    .withQuoteChar(quote)
                    .withLineSeparator(lineSep)).writeValueAsString(object);

            stringList.add(csv);
        } catch (JsonProcessingException e) {
            System.out.println(e);
        }
    }

    return stringList;
}

诀窍是获取列表中的一个元素。为了避免崩溃,我在开头添加了一些数据完整性测试,在输入列表中没有项目的情况下返回一个不可修改的空列表。 然后检索Object的实例并使用它来获取类。

或者如果convertToString方法在参数化类中,你可以用稍微不同的方式做到这一点

public class GenericClass<T> {

 private final Class<T> type;

 public GenericClass(Class<T> type) {
      this.type = type;
 }

 public Class<T> getMyType() {
     return this.type;
 }
}

这个解决方案允许你获得T的课程。我不认为你会为这个问题需要它,但它可能会派上用场。

答案 1 :(得分:1)

我创建了一个类似于下面的CSVUtil类,它使用java反射。

在CSVUtil下使用的示例 假设POJO学生,

List<Student> StudentList = new ArrayList<Student>();
String StudentCSV = CSVUtil.toCSV(StudentList,' ',false);


import java.lang.reflect.Field;
import java.util.List;
import java.util.logging.Logger;

CSVUtil类

public class CSVUtil {
    private static final Logger LOGGER = Logger.getLogger(CSVUtil.class .getName());
    private final static char DEFAULT_SEPARATOR = ' ';

    public static String toCSV(List<?> objectList, char separator, boolean displayHeader) {

        StringBuilder result =new StringBuilder();  
        if (objectList.size() == 0) {
            return result.toString();
        }   

        if(displayHeader){
            result.append(getHeaders(objectList.get(0),separator)); 
            result.append("\n");
        }

        for (Object obj : objectList) {
            result.append(addObjectRow(obj, separator)).append("\n");
        }

        return result.toString();
    }

    public static String getHeaders(Object obj,char seperator) {
        StringBuilder resultHeader = new StringBuilder(); 
        boolean firstField = true;
        Field fields[] = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String value;
            try {
                value = field.getName();

                if(firstField){
                     resultHeader.append(value);
                     firstField = false;
                 }
                 else{
                    resultHeader.append(separator).append(value);
                 }
                field.setAccessible(false);
            } catch (IllegalArgumentException  e) {
                LOGGER.severe(e.toString());
            }
        }           
      return resultHeader.toString();

    }


    public static String addObjectRow(Object obj, char separator) {

        StringBuilder csvRow =new StringBuilder();  
        Field fields[] = obj.getClass().getDeclaredFields();
        boolean firstField = true;
        for (Field field : fields) {
            field.setAccessible(true);
            Object value;
            try {
                value = field.get(obj);
                if(value == null)
                    value = "";
                if(firstField){
                    csvRow.append(value);
                    firstField = false;
                }
                else{
                    csvRow.append(separator).append(value);
                }
                field.setAccessible(false);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                LOGGER.severe(e.toString());
            }
        }
        return csvRow.toString();
    }
}

答案 2 :(得分:0)

看起来这个问题比大多数人想要的更加困难,因为Java做泛型。 Bruno的回答显示了如果您可以做出某些假设或者可以以某种方式构建代码的话可能有用的选项。

可以通过对其他问题的回答找到适合您案例的另一个选项:How to get a class instance of generics type T

在那里,您可以找到文章的链接:http://blog.xebia.com/acessing-generic-types-at-runtime-in-java/

这描述了如何使用对象超类的ParameterizedType。您可以将其应用于List对象,希望它对您有用。在这种情况下,这只能幸运地工作,因为您将具有超类的对象作为参数,其类型参数与您所需的类型相匹配。

通常,我们不能依赖于在运行时知道类型参数。我们最多可以使用类型标记(Class<T>类型的参数)