使用 Jackson 流式 API 在 Spring Boot 中高效处理大型 JSON

本文我们将探讨如何在 Spring Boot 应用程序中使用 Jackson 的流式 API(JsonParser)高效处理大型 JSON 对象。通过增量反序列化大型 JSON 对象,可以避免内存和性能瓶颈。本文将通过一个实时示例,演示如何处理大型 JSON 负载,并实现数据的增量处理和过滤。我们将实现一个 Spring Boot 控制器来处理大型 JSON 输入,并使用 Postman 进行测试,提供示例输入和输出。该解决方案确保低内存消耗和高性能,特别适用于包含数百万条记录的 JSON 文件

为了在 Spring Boot 应用程序中使用 Jackson 的 JsonParser(流式 API)高效处理大型 JSON 对象,我们可以实现一种增量反序列化的解决方案,以避免性能和内存消耗问题。我将通过一个实时示例为您讲解,并展示如何使用 Postman 进行测试。


场景

假设我们正在处理一个包含数百万条产品详细信息的大型 JSON 文件,如果一次性加载整个文件,可能会导致内存问题。我们希望增量处理该文件,并仅返回部分数据(例如,符合特定条件的产品)给客户端。

实现步骤

1. Spring Boot 配置

Maven 依赖
在 pom.xml 中添加以下 Jackson 依赖:

<dependencies>
    <!-- Jackson for JSON Processing -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
</dependencies>

 

2. 处理 JSON 流式数据的控制器

创建一个 Spring Boot 控制器来处理传入的请求。

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @PostMapping("/process-large-json")
    public ResponseEntity<List<Product>> processLargeJson(@RequestBody InputStream inputStream) {
        List<Product> matchingProducts = new ArrayList<>();
        
        try (JsonParser jsonParser = new JsonFactory().createParser(inputStream)) {
            // Start parsing JSON
            if (jsonParser.nextToken() == JsonToken.START_ARRAY) {
                while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
                    Product product = readProduct(jsonParser);

                    // Filter logic - Example: Only process products with price > 100
                    if (product != null && product.getPrice() > 100) {
                        matchingProducts.add(product);
                    }
                }
            }
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
        }

        return ResponseEntity.ok(matchingProducts);
    }

    // Utility method to read each product incrementally
    private Product readProduct(JsonParser jsonParser) throws IOException {
        Product product = new Product();
        while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
            String fieldName = jsonParser.getCurrentName();
            jsonParser.nextToken(); // move to value

            if ("id".equals(fieldName)) {
                product.setId(jsonParser.getIntValue());
            } else if ("name".equals(fieldName)) {
                product.setName(jsonParser.getText());
            } else if ("price".equals(fieldName)) {
                product.setPrice(jsonParser.getDoubleValue());
            }
        }
        return product;
    }
}

3. 产品模型

创建一个简单的 Product 类来保存解析后的数据。

public class Product {
    private int id;
    private String name;
    private double price;

    // Getters and setters
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public double getPrice() { return price; }
    public void setPrice(double price) { this.price = price; }
}

4. 用于测试的大型 JSON 输入示例

以下是一个大型 JSON 输入示例(包含多个产品对象)。您可以将其保存到文件中,或直接在 Postman 中输入。

[
    { "id": 1, "name": "Product 1", "price": 50 },
    { "id": 2, "name": "Product 2", "price": 150 },
    { "id": 3, "name": "Product 3", "price": 200 },
    { "id": 4, "name": "Product 4", "price": 80 },
    { "id": 5, "name": "Product 5", "price": 120 }
]

5. 使用 Postman 进行测试

端点: POST /api/products/process-large-json

请求体: Raw JSON 数据

在 Postman 中,按照以下步骤操作:

  1. 选择 POST 方法。

  2. 设置 URL 为 http://localhost:8080/api/products/process-large-json

  3. 在 Body 选项卡中,选择 raw,然后选择 JSON

  4. 粘贴大型 JSON 数组(示例见上文)。

  5. 点击 Send

预期输出:

价格大于 100 的产品将被返回。以下是过滤后的输出示例:

[
    { "id": 2, "name": "Product 2", "price": 150 },
    { "id": 3, "name": "Product 3", "price": 200 },
    { "id": 5, "name": "Product 5", "price": 120 }
]

解释

  • JsonParser: 流式 API(JsonParser)逐个处理 JSON 令牌,而无需将整个文件加载到内存中,非常适合处理大型数据。

  • 增量处理: Product 对象被增量反序列化。仅符合特定条件(例如,价格 > 100)的对象会被存储在内存中并返回,从而减少内存使用。

  • InputStream: 传入的请求体作为 InputStream 处理,使我们能够直接处理大型 JSON 对象。

该解决方案高效处理大型 JSON 负载,同时确保内存使用保持在较低水平。

请登录后发表评论

    没有回复内容