如何用Jackson CSV Mapper将ElasticSearch响应写入CSV?
问题:Jackson将含嵌套对象的ElasticSearch响应写入CSV失败
我从ElasticSearch获取了包含大量嵌套对象的响应,尝试用Jackson将其写入CSV。响应数据封装在如下Java record中:
public record SearchRecord(String id, @JsonUnwrapped Map<String, Object> values) {}
实际结果存储在values属性中,根据Jackson文档,它应可作为Map<String, Object>处理。我困惑于如何基于上述record配置CSV mapper。
我尝试了以下代码:
@SneakyThrows public void writeResults(final List<SearchRecord> searchRecords) { final CsvMapper csvMapper = CsvMapper.builder().build(); csvMapper.writerWithSchemaFor(SearchRecord.class) .with(CsvSchema.emptySchema()) .writeValue(new File("./output.csv"), searchRecords); }
但运行时抛出异常:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Incompatible types: declared root type ([simple type, class com.example.SearchRecord]) vs `java.util.LinkedList`
我在GitHub找到类似问题的issue,其建议使用@JsonUnwrapped但未解决问题,请问还有其他方案吗?
解决方案
1. 解决类型不匹配的核心问题
抛出的异常本质是:你用writerWithSchemaFor(SearchRecord.class)声明要序列化单个SearchRecord对象,但实际传入的是List<SearchRecord>集合,类型不匹配导致报错。
2. 正确配置CSV Mapper与Schema
结合@JsonUnwrapped的Map特性,需要让Jackson自动推导所有字段的CSV Schema,并明确指定集合类型。修改后的代码如下:
@SneakyThrows public void writeResults(final List<SearchRecord> searchRecords) { final CsvMapper csvMapper = CsvMapper.builder().build(); // 生成适配SearchRecord的Schema,启用未包装属性展开,同时添加表头 CsvSchema csvSchema = csvMapper.schemaFor(SearchRecord.class) .withHeader() .withUnwrapped(true); // 明确指定要序列化的是List<SearchRecord>类型 csvMapper.writerFor(new TypeReference<List<SearchRecord>>() {}) .with(csvSchema) .writeValue(new File("./output.csv"), searchRecords); }
3. 处理嵌套对象的扩展场景
如果values中的Object包含嵌套对象,默认Jackson会把嵌套对象序列化为JSON字符串。如果需要将嵌套对象的字段也展开为CSV列,可以做以下处理:
- 提前递归扁平化
values:将嵌套的Map/对象转换为单层键值对,键用点分隔(比如user.info.age) - 为嵌套对象编写自定义序列化器,让Jackson将其展开为多个CSV列
内容的提问来源于stack exchange,提问作者mayonnaisebrightfish
相关产品推荐
相关产品推荐

