You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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:

  1. 定义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方法
}
  1. 转换逻辑实现
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 08:20:22