我的用例是编写一个通用的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;
}
我正在使用杰克逊-数据格式-csv 库,但我被困住了!HOW_TO!部分,即如何从对象列表中提取对象的.class。我正在学习并遇到了类型擦除,所以我认为除了将.class作为我的函数的参数之外,这在某种程度上是不可能的。但是我也使用 Java 反射从通用实体中提取此对象列表,因此我无法选择提供.class参数。
有解决方法吗?
或
我可以转换具有添加分隔符、引号字符、行分隔符等功能的通用List<T> objectList to List<String> csvList
的任何其他方法/库。
谢谢!
我创建了一个类似于下面的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 separator) {
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();
}
}
有一个简单的选项。我在您的代码中添加了一些行来显示它:
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 类。我认为这个问题不需要它,但它可能会派上用场。
由于Java如何做泛型,这个问题似乎比大多数人希望的要难。 Bruno 的回答显示了如果您可以做出某些假设或可以以某种方式构建代码,则可能有效的选项。
另一个应该适用于您的案例的选项可以通过另一个问题的答案找到:如何获取泛型类型 T 的类实例
在那里,您会找到一篇文章的链接:http://blog.xebia.com/acessing-generic-types-at-runtime-in-java/
这描述了如何使用对象的超类的参数化类型。 您可以将其应用于您的List
对象,希望它对您有用。 幸运的是,这仅适用于这种情况,因为您将具有超类的对象作为参数,其类型参数与您需要的参数匹配。
,我们不能依赖于在运行时知道类型参数。 我们充其量可以使用类型标记(类型 Class<T>
的参数)