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

如何同时读取两个结构不同的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;
    }
}

方案优势

  1. 异构适配性强:使用JsonNode树模型,无需依赖固定实体类,findValuesAsText方法会递归遍历整个JSON结构,自动定位所有productID字段,不管其所在层级和数组结构。
  2. 代码整合彻底:将原ProductIdImporter中的ObjectMapper配置和Main类的业务逻辑结合,统一解析规则。
  3. 鲁棒性提升:配置FAIL_ON_UNKNOWN_PROPERTIES=false忽略未知字段,避免因JSON结构差异抛出异常。
  4. 逻辑清晰:拆分extractProductIds方法专门负责收集ID,与业务方法解耦,便于维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 09:36:10