如何创建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
相关产品推荐
相关产品推荐

