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

如何创建ItemReader读取非数组格式JSON并遍历foo字段(Spring Batch)

解决Spring Batch读取嵌套数组JSON的重复读取问题

针对你遇到的非顶层数组JSON(结构为{ foo: [], bar: {}, ... }),需要读取foo字段数组并避免重复读取的问题,这里提供两种可行方案:

方案一:小文件场景 - 一次性加载后迭代

如果JSON文件体积不大,可以先将整个文件映射到POJO,再通过迭代器逐个返回foo数组元素,从根源避免重复解析文件。

1. 定义对应JSON结构的POJO

// 顶层JSON对应的POJO
public class RootData {
    private List<FooItem> foo;
    // 其他字段如bar、baz可按需定义,或用@JsonIgnore忽略不需要的字段
    
    public List<FooItem> getFoo() {
        return foo;
    }

    public void setFoo(List<FooItem> foo) {
        this.foo = foo;
    }
}

// foo数组元素对应的POJO
public class FooItem {
    // 按实际JSON结构定义字段及getter/setter
}

2. 自定义ItemReader实现

import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.util.Iterator;

public class FooItemReader implements ItemReader<FooItem> {
    private final ObjectMapper objectMapper;
    private final String filePath;
    private Iterator<FooItem> fooIterator;

    public FooItemReader(ObjectMapper objectMapper, String filePath) {
        this.objectMapper = objectMapper;
        this.filePath = filePath;
    }

    @Override
    public FooItem read() throws Exception {
        // 仅初始化一次迭代器,避免重复解析整个文件
        if (fooIterator == null) {
            RootData rootData = objectMapper.readValue(new File(filePath), RootData.class);
            fooIterator = rootData.getFoo().iterator();
        }
        // 有元素则返回,无元素返回null(Spring Batch任务结束标志)
        return fooIterator.hasNext() ? fooIterator.next() : null;
    }
}

3. 配置Spring Batch Job

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration
public class BatchConfig {

    @Bean
    public FooItemReader fooItemReader(ObjectMapper objectMapper) {
        return new FooItemReader(objectMapper, "your/json/file/path.json");
    }

    // 后续配置Step、Job时,将该Reader传入即可
}

方案二:大文件场景 - 流式解析避免内存溢出

如果JSON文件体积较大,一次性加载会占用过多内存,可使用Jackson流式API定位到foo数组后,逐个解析元素:

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.batch.item.ItemReader;
import java.io.File;

public class StreamingFooItemReader implements ItemReader<FooItem> {
    private final ObjectMapper objectMapper;
    private final String filePath;
    private JsonParser jsonParser;
    private boolean reachedFooArray;

    public StreamingFooItemReader(ObjectMapper objectMapper, String filePath) {
        this.objectMapper = objectMapper;
        this.filePath = filePath;
    }

    @Override
    public FooItem read() throws Exception {
        // 初始化流式解析器,定位到foo数组
        if (jsonParser == null) {
            JsonFactory factory = objectMapper.getFactory();
            jsonParser = factory.createParser(new File(filePath));
            // 跳过顶层对象的其他字段,直到找到foo数组
            while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
                String fieldName = jsonParser.getCurrentName();
                if ("foo".equals(fieldName)) {
                    jsonParser.nextToken(); // 进入foo数组内部
                    reachedFooArray = true;
                    break;
                }
                // 跳过非foo字段的所有子节点
                jsonParser.skipChildren();
            }
        }

        // 逐个解析foo数组中的元素
        if (reachedFooArray && jsonParser.nextToken() == JsonToken.START_OBJECT) {
            return objectMapper.readValue(jsonParser, FooItem.class);
        } else {
            // 解析完成后关闭资源,返回null结束任务
            if (jsonParser != null) {
                jsonParser.close();
            }
            return null;
        }
    }
}

重复读取问题的根源

你之前出现重复读取,大概率是因为每次调用read()方法时都重新解析了整个JSON文件,导致每次都从foo数组的第一个元素开始返回。上述两种方案都保证了解析逻辑仅初始化一次,每次read()只返回下一个元素,彻底解决重复读取问题。

内容的提问来源于stack exchange,提问作者AGM

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 22:36:39