如何同时读取两个结构不同的JSON文件并收集productID
异构JSON文件收集ProductID并整合代码的实现方案
任务说明
需要从两个数据结构不同的JSON文件中收集所有productID到列表,再将该列表传入接收JSON文件目录的自定义方法。两个JSON文件的字段数量、顺序存在差异,productID所在的数组结构也不同,属于异构JSON解析场景。
现有代码
Main类代码
public class Main { public static void main(String[] args) throws IOException, ParseException { JSONParser parser = new JSONParser(); InputStream isOne = JSONParser.class.getResourceAsStream("/test/java/resources/json/file/product_0001690510.json"); InputStream isTwo = JSONParser.class.getResourceAsStream("/test/java/resources/json/file/product_0001694109.json"); // ... need to use somehow InpuStream for reading two JSON files with different structure inside JSONArray arr = obj.getJSONArray("products"); // notice that `"products": [...]` String productId = null; for (int i = 0; i < arr.length(); i++) { productId = arr.getJSONObject(i).getString("productID"); } List<String> productIds = new ArrayList<>(Collections.singleton(productId)); for (var file : obj.keySet()) { System.out.println(getAllExportsWithProductIds(file, productIds)); } } public static List<String> getAllExportsWithProductIds(String directory, List<String> productIds) throws IOException { var matchingObjects = new ArrayList<String>(); try (var fileStream = Files.walk(Path.of(directory))) { for (var file : fileStream.toList()) { var json = Json.readString(Files.readString(file)); var objects = JsonDecoder.array(json); for (var object : objects) { var objectProductIDs = JsonDecoder.field( object, "products", JsonDecoder.array(JsonDecoder.field("productID", JsonDecoder::string)) ); for (var productId : objectProductIDs) { if (productIds.contains(productId)) { matchingObjects.add(Json.writeString(object)); break; } } } } } return matchingObjects; } }
已实现的ProductIdImporter类代码
public class ProductIdImporter { public ProductIdImporter() { // TODO Auto-generated constructor stub } public void importJson() { List<Path> paths = Arrays.asList(Paths.get("C:\\Users\\pc\\IdeaProjects\\jsonapi\\src\\test\\java\\resources\\json\\product_0001690510.json"), Paths.get("C:\\Users\\pc\\IdeaProjects\\jsonapi\\src\\test\\java\\resources\\json\\product_0001694109.json")); ObjectMapper jsonMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); Set<String> productIds = paths.stream().map(path -> { try { return jsonMapper.readValue(Files.newInputStream(path), ExportList[].class); } catch (Exception e) { throw new RuntimeException(e); } }).map(Arrays::asList) .flatMap(List::stream) .map(ExportList::productList) .flatMap(List::stream) .map(Product::getId) .collect(Collectors.toSet()); productIds.forEach(System.out::println); } public static void main(String[] args) { ProductIdImporter importer = new ProductIdImporter(); importer.importJson(); } static class ExportList { public List<Product> products; public ExportList() { } public List<Product> productList() { return products; } } static class Product { public String productID; public Product() { } public String getId() { return productID; } } }
问题
能否同时读取所有给定的JSON文件以收集productID到列表?若可以,在两个JSON文件数据结构不同的情况下该如何实现?
补充说明
“数据结构不同”指字段数量、顺序存在差异,例如两个JSON文件中的productID字段,以及数组对象的结构不同。这类异构JSON解析难度更高,需针对性处理。
具体实现方案
利用Jackson的**树模型(JsonNode)**可以灵活处理异构JSON,无需为每个结构定义实体类,同时整合原有代码的功能:
整合后的完整代码
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class Main { // 全局ObjectMapper,配置忽略未知字段,适配异构JSON private static final ObjectMapper objectMapper = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); public static void main(String[] args) throws IOException { // 加载两个JSON文件的输入流 InputStream isOne = Main.class.getResourceAsStream("/test/java/resources/json/file/product_0001690510.json"); InputStream isTwo = Main.class.getResourceAsStream("/test/java/resources/json/file/product_0001694109.json"); // 收集所有productID List<String> productIds = new ArrayList<>(); productIds.addAll(extractProductIds(isOne)); productIds.addAll(extractProductIds(isTwo)); // 传入自定义方法(替换为你的目标目录路径) String targetDirectory = "path/to/your/target/json/directory"; List<String> matchingExports = getAllExportsWithProductIds(targetDirectory, productIds); matchingExports.forEach(System.out::println); } /** * 从输入流中提取所有productID,支持任意JSON结构 */ private static List<String> extractProductIds(InputStream inputStream) throws IOException { JsonNode rootNode = objectMapper.readTree(inputStream); // 递归遍历整个JSON树,找到所有名为productID的字段 return rootNode.findValuesAsText("productID").stream() .filter(id -> id != null && !id.isEmpty()) .collect(Collectors.toList()); } /** * 原自定义方法,改用Jackson树模型适配异构JSON */ public static List<String> getAllExportsWithProductIds(String directory, List<String> productIds) throws IOException { var matchingObjects = new ArrayList<String>(); try (var fileStream = Files.walk(Path.of(directory))) { for (var file : fileStream.toList()) { // 跳过非JSON文件和目录 if (!Files.isRegularFile(file) || !file.getFileName().toString().endsWith(".json")) { continue; } JsonNode rootNode = objectMapper.readTree(Files.readString(file)); // 处理根节点是数组或包含products数组的情况 JsonNode objectsNode = rootNode.isArray() ? rootNode : rootNode.findPath("products"); for (JsonNode object : objectsNode) { // 提取当前对象下的所有productID List<String> objectProductIDs = object.findValuesAsText("productID"); for (String productId : objectProductIDs) { if (productIds.contains(productId)) { matchingObjects.add(objectMapper.writeValueAsString(object)); break; } } } } } return matchingObjects; } }
方案优势
- 异构适配性强:使用
JsonNode树模型,无需依赖固定实体类,findValuesAsText方法会递归遍历整个JSON结构,自动定位所有productID字段,不管其所在层级和数组结构。 - 代码整合彻底:将原
ProductIdImporter中的ObjectMapper配置和Main类的业务逻辑结合,统一解析规则。 - 鲁棒性提升:配置
FAIL_ON_UNKNOWN_PROPERTIES=false忽略未知字段,避免因JSON结构差异抛出异常。 - 逻辑清晰:拆分
extractProductIds方法专门负责收集ID,与业务方法解耦,便于维护。
内容的提问来源于stack exchange,提问作者invzbl3
相关产品推荐
相关产品推荐

