Spring Boot中用JOLT将复杂JSON转CSV的实现方案咨询
JOLT转换规格及Spring Boot实现方案
一、JOLT转换规格(Spec)
先将嵌套JSON扁平化,转换成适合导出CSV的一维JSON数组,再通过CSV工具导出。以下是适配你提供的JSON的JOLT规格:
JOLT Spec代码
[ { "operation": "shift", "spec": { "bigRecords": { "*": { "Records": { "Name": "records[&2].record_name", "Id": "records[&2].record_id", "Type": "records[&2].record_type", "Area": "records[&2].record_area", "*": "records[&2].&" }, "ancestors": { "*": { "Name": "records[&3].sub_name", "Type": "records[&3].sub_type", "Id": "records[&3].sub_id", "*": "records[&3].sub_&" } }, "dependentRecords": { "*": { "Name": "records[&3].sub_name", "Type": "records[&3].sub_type", "Id": "records[&3].sub_id", "*": "records[&3].sub_&" } } } } } }, { "operation": "default", "spec": { "records": { "*": { "sub_name": "", "sub_type": "", "sub_sta": "", "sub_some": "", "sub_id": "" } } } } ]
转换效果
上述规格会将原始JSON转换为如下结构的数组,每个元素对应CSV的一行:
{ "records": [ { "record_name": "R1", "record_id": "1P02XV425KCASEZGQLB8LPQMF4ZXCUBCTA01", "record_type": "New", "record_area": "Eng", "createdAt": "11-10-2022 06:10:00", "deletedDate": "11-10-2022 06:20:00", "deleted": true, "deletedBy": "userId", "createdBy": "userId", "sub_name": "P1", "sub_type": "DELETE", "sub_sta": "", "sub_some": "", "sub_id": "1" }, { "record_name": "R2", "record_id": "1P02XV425KCASEZGQLB8LPQMF4ZXCUBCTA01", "record_type": "DRAFT", "record_area": "Eng", "createdAt": "11-10-2022 06:10:00", "deletedDate": "11-10-2022 06:20:00", "deleted": true, "deletedBy": "userId", "createdBy": "userId", "sub_name": "P3", "sub_type": "DELETE", "sub_sta": "1P0", "sub_some": "1P02", "sub_id": "1P0" } ] }
二、Spring Boot中JSON转CSV的其他方法
方法1:Jackson + OpenCSV
通过实体类映射,结合OpenCSV导出CSV:
- 定义CSV实体类
import com.opencsv.bean.CsvBindByName; public class RecordCsvDto { @CsvBindByName(column = "记录名称") private String recordName; @CsvBindByName(column = "记录ID") private String recordId; @CsvBindByName(column = "记录类型") private String recordType; @CsvBindByName(column = "区域") private String recordArea; @CsvBindByName(column = "创建时间") private String createdAt; @CsvBindByName(column = "删除时间") private String deletedDate; @CsvBindByName(column = "是否删除") private Boolean deleted; @CsvBindByName(column = "删除人") private String deletedBy; @CsvBindByName(column = "创建人") private String createdBy; @CsvBindByName(column = "子项名称") private String subName; @CsvBindByName(column = "子项类型") private String subType; @CsvBindByName(column = "子项sta") private String subSta; @CsvBindByName(column = "子项some") private String subSome; @CsvBindByName(column = "子项ID") private String subId; // 生成getter、setter方法 }
- 转换逻辑实现
import com.fasterxml.jackson.databind.ObjectMapper; import com.opencsv.bean.StatefulBeanToCsv; import com.opencsv.bean.StatefulBeanToCsvBuilder; import org.springframework.stereotype.Component; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import java.util.Map; @Component public class JsonToCsvConverter { private final ObjectMapper objectMapper; public JsonToCsvConverter(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } public String convert(String json) throws Exception { Map<String, List<Map<String, Object>>> rootMap = objectMapper.readValue(json, Map.class); List<Map<String, Object>> bigRecords = rootMap.get("bigRecords"); List<RecordCsvDto> csvDtos = new ArrayList<>(); for (Map<String, Object> bigRecord : bigRecords) { Map<String, Object> records = (Map<String, Object>) bigRecord.get("Records"); RecordCsvDto dto = new RecordCsvDto(); // 填充主记录字段 dto.setRecordName((String) records.get("Name")); dto.setRecordId((String) records.get("Id")); dto.setRecordType((String) records.get("Type")); dto.setRecordArea((String) records.get("Area")); dto.setCreatedAt((String) records.get("createdAt")); dto.setDeletedDate((String) records.get("deletedDate")); dto.setDeleted((Boolean) records.get("deleted")); dto.setDeletedBy((String) records.get("deletedBy")); dto.setCreatedBy((String) records.get("createdBy")); // 填充子记录字段 Map<String, Object> subRecord = null; if (bigRecord.containsKey("ancestors")) { subRecord = ((List<Map<String, Object>>) bigRecord.get("ancestors")).get(0); } else if (bigRecord.containsKey("dependentRecords")) { subRecord = ((List<Map<String, Object>>) bigRecord.get("dependentRecords")).get(0); } if (subRecord != null) { dto.setSubName((String) subRecord.get("Name")); dto.setSubType((String) subRecord.get("Type")); dto.setSubSta((String) subRecord.get("sta")); dto.setSubSome((String) subRecord.get("some")); dto.setSubId((String) subRecord.get("Id")); } csvDtos.add(dto); } // 导出CSV StringWriter writer = new StringWriter(); StatefulBeanToCsv<RecordCsvDto> beanToCsv = new StatefulBeanToCsvBuilder<RecordCsvDto>(writer) .withSeparator(',') .build(); beanToCsv.write(csvDtos); return writer.toString(); } }
方法2:Apache Commons CSV
无需实体类,直接手动拼接CSV内容:
import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; import org.springframework.stereotype.Component; import java.io.StringWriter; import java.util.List; import java.util.Map; @Component public class CommonsCsvConverter { public String convert(String json) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); Map<String, List<Map<String, Object>>> rootMap = objectMapper.readValue(json, Map.class); List<Map<String, Object>> bigRecords = rootMap.get("bigRecords"); StringWriter writer = new StringWriter(); String[] headers = {"记录名称", "记录ID", "记录类型", "区域", "创建时间", "删除时间", "是否删除", "删除人", "创建人", "子项名称", "子项类型", "子项sta", "子项some", "子项ID"}; CSVPrinter printer = new CSVPrinter(writer, CSVFormat.DEFAULT.withHeader(headers)); for (Map<String, Object> bigRecord : bigRecords) { Map<String, Object> records = (Map<String, Object>) bigRecord.get("Records"); Map<String, Object> subRecord = null; if (bigRecord.containsKey("ancestors")) { subRecord = ((List<Map<String, Object>>) bigRecord.get("ancestors")).get(0); } else if (bigRecord.containsKey("dependentRecords")) { subRecord = ((List<Map<String, Object>>) bigRecord.get("dependentRecords")).get(0); } printer.printRecord( records.get("Name"), records.get("Id"), records.get("Type"), records.get("Area"), records.get("createdAt"), records.get("deletedDate"), records.get("deleted"), records.get("deletedBy"), records.get("createdBy"), subRecord != null ? subRecord.get("Name") : "", subRecord != null ? subRecord.get("Type") : "", subRecord != null ? subRecord.get("sta") : "", subRecord != null ? subRecord.get("some") : "", subRecord != null ? subRecord.get("Id") : "" ); } printer.flush(); return writer.toString(); } }
内容的提问来源于stack exchange,提问作者Aman Mishra
相关产品推荐
相关产品推荐

