
本教程详细探讨了在spring boot应用中如何高效地从复杂嵌套json结构中提取特定数据。我们将重点介绍jackson库的两种核心方法:jackson streaming api,适用于处理大型或结构动态的json,以及jackson data binding,适用于将json映射到预定义java对象。文章将提供详细的代码示例和实现步骤,并讨论如何进一步过滤提取出的数据,例如按类别筛选。
在Spring Boot应用程序中处理来自API调用的JSON数据是常见的任务,尤其当JSON结构包含多层嵌套时,如何高效且优雅地提取所需信息成为一个关键问题。本文将深入探讨使用Jackson库来解决这一挑战的两种主要方法:Jackson Streaming API和Jackson Data Binding,并提供具体的代码示例。
在Spring Boot项目中,Jackson是默认的JSON处理库,因此通常无需额外添加依赖。但如果需要确保版本或解决特定问题,可以检查 pom.xml 中是否包含以下依赖:
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>假设我们有以下JSON数据,其中包含嵌套的 footwearList 和 clothingList:
[
    {
        "id": 1,
        "footwearList": [
            {
                "id": 1,
                "name": "sandals",
                "category": "men"
            },
            {
                "id": 3,
                "name": "sandals",
                "category": "women"
            }
        ],
        "clothingList": [
            {
                "id": 1,
                "name": "t-shirt",
                "category": "men"
            },
            {
                "id": 3,
                "name": "tshirt",
                "category": "women"
            }
        ]
    },
    {
        "id": 2,
        "footwearList": [
            {
                "id": 2,
                "name": "shoes",
                "category": "men"
            },
            {
                "id": 4,
                "name": "shoes",
                "category": "women"
            }
        ],
        "clothingList": [
            {
                "id": 2,
                "name": "shirt",
                "category": "men"
            },
            {
                "id": 4,
                "name": "shirt",
                "category": "women"
            }
        ]
    }
]我们的目标是从这个JSON中提取 footwearList 和 clothingList 中的 Item 对象,并可能进一步按 category 进行筛选。
当JSON结构非常庞大、键名动态不固定,或者我们不希望将整个JSON完全映射为Java对象时,Jackson Streaming API提供了一种高效的迭代方式。它允许我们遍历JSON树,按需提取数据。
首先,定义一个 Item 类来表示 footwearList 和 clothingList 中的元素:
public class Item {
    private int id;
    private String name;
    private String category;
    // 无参构造函数
    public Item() {}
    // 带参构造函数
    public Item(int id, String name, String category) {
        this.id = id;
        this.name = name;
        this.category = category;
    }
    // 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 String getCategory() { return category; }
    public void setCategory(String category) { this.category = category; }
    @Override
    public String toString() {
        return "Item{id=" + id + ", name='" + name + "', category='" + category + "'}";
    }
}接下来,我们创建一个辅助方法将 JsonNode 转换为 Item 对象:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonExtractor {
    private static final ObjectMapper MAPPER = new ObjectMapper();
    // 辅助方法:将JsonNode转换为Item对象
    public static Item nodeToItem(JsonNode node) {
        try {
            return MAPPER.treeToValue(node, Item.class);
        } catch (com.fasterxml.jackson.core.JsonProcessingException e) {
            System.err.println("Error converting JsonNode to Item: " + e.getMessage());
            return null;
        }
    }
    // ... 其他方法
}现在,我们可以编写逻辑来遍历JSON并提取嵌套列表中的 Item:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import java.util.regex.Pattern;
import java.util.function.Predicate;
import java.util.stream.StreamSupport;
import java.util.Collections; // 导入Collections
public class JsonExtractor {
    private static final ObjectMapper MAPPER = new ObjectMapper();
    // 定义一个正则表达式谓词,用于匹配包含"footwear"或"clothing"的字段名
    public static final Predicate<String> TARGET_LIST_PREDICATE = Pattern.compile("footwear|clothing").asPredicate();
    // 辅助方法:将JsonNode转换为Item对象 (同上)
    public static Item nodeToItem(JsonNode node) {
        try {
            return MAPPER.treeToValue(node, Item.class);
        } catch (com.fasterxml.jackson.core.JsonProcessingException e) {
            System.err.println("Error converting JsonNode to Item: " + e.getMessage());
            return null;
        }
    }
    public List<Item> extractNestedItems(String json) {
        try {
            JsonNode rootNode = MAPPER.readTree(json);
            if (!rootNode.isArray()) {
                System.err.println("Root JSON node is not an array.");
                return Collections.emptyList();
            }
            return StreamSupport.stream(rootNode.spliterator(), false) // 遍历根数组的每个对象
                .<JsonNode>mapMulti((node, consumer) -> { // 对于每个对象
                    node.fields().forEachRemaining(entry -> { // 遍历其所有字段
                        if (TARGET_LIST_PREDICATE.test(entry.getKey()) && entry.getValue().isArray()) {
                            // 如果字段名匹配且其值为数组,则将数组中的每个元素传递给consumer
                            entry.getValue().forEach(consumer);
                        }
                    });
                })
                .map(JsonExtractor::nodeToItem) // 将每个JsonNode转换为Item对象
                .toList(); // 收集结果
        } catch (com.fasterxml.jackson.core.JsonProcessingException e) {
            System.err.println("Error parsing JSON: " + e.getMessage());
            return Collections.emptyList();
        }
    }
    // 示例用法
    public static void main(String[] args) {
        String json = "[\n" +
                "    {\n" +
                "        \"id\": 1,\n" +
                "        \"footwearList\": [\n" +
                "            { \"id\": 1, \"name\": \"sandals\", \"category\": \"men\" },\n" +
                "            { \"id\": 3, \"name\": \"sandals\", \"category\": \"women\" }\n" +
                "        ],\n" +
                "        \"clothingList\": [\n" +
                "            { \"id\": 1, \"name\": \"t-shirt\", \"category\": \"men\" },\n" +
                "            { \"id\": 3, \"name\": \"tshirt\", \"category\": \"women\" }\n" +
                "        ]\n" +
                "    },\n" +
                "    {\n" +
                "        \"id\": 2,\n" +
                "        \"footwearList\": [\n" +
                "            { \"id\": 2, \"name\": \"shoes\", \"category\": \"men\" },\n" +
                "            { \"id\": 4, \"name\": \"shoes\", \"category\": \"women\" }\n" +
                "        ],\n" +
                "        \"clothingList\": [\n" +
                "            { \"id\": 2, \"name\": \"shirt\", \"category\": \"men\" },\n" +
                "            { \"id\": 4, \"name\": \"shirt\", \"category\": \"women\" }\n" +
                "        ]\n" +
                "    }\n" +
                "]";
        JsonExtractor extractor = new JsonExtractor();
        List<Item> allItems = extractor.extractNestedItems(json);
        System.out.println("所有提取的Item:");
        allItems.forEach(System.out::println);
        // 进一步按类别过滤
        List<Item> menItems = allItems.stream()
                                      .filter(item -> "men".equals(item.getCategory()))
                                      .toList();
        System.out.println("\n按类别'men'过滤的Item:");
        menItems.forEach(System.out::println);
    }
}输出示例:
 
                        Easily find JSON paths within JSON objects using our intuitive Json Path Finder
 30
30
                             
                    所有提取的Item:
Item{id=1, name='sandals', category='men'}
Item{id=3, name='sandals', category='women'}
Item{id=1, name='t-shirt', category='men'}
Item{id=3, name='tshirt', category='women'}
Item{id=2, name='shoes', category='men'}
Item{id=4, name='shoes', category='women'}
Item{id=2, name='shirt', category='men'}
Item{id=4, name='shirt', category='women'}
按类别'men'过滤的Item:
Item{id=1, name='sandals', category='men'}
Item{id=1, name='t-shirt', category='men'}
Item{id=2, name='shoes', category='men'}
Item{id=2, name='shirt', category='men'}这种方法非常灵活,即使JSON的顶层结构或嵌套列表的键名发生变化,只要我们调整 TARGET_LIST_PREDICATE,代码仍然可以工作。
对于结构相对固定且已知其模式的JSON数据,Jackson Data Binding是更推荐和简洁的方法。它允许我们将JSON直接映射到Java对象(POJO),极大地简化了数据访问。
首先,定义一个 Store 类来表示JSON中的顶层对象。由于 footwearList 和 clothingList 的键是动态的,我们可以使用 Map<String, List<Item>> 结合 @JsonAnySetter 和 @JsonAnyGetter 来处理。
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Store {
    private int id;
    private Map<String, List<Item>> items = new HashMap<>(); // 用于存储动态的列表
    // 无参构造函数
    public Store() {}
    // Getters and Setters for id
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    // @JsonAnySetter 用于反序列化时捕获未映射的键值对
    @JsonAnySetter
    public void readStore(String key, List<Item> value) {
        this.items.put(key, value);
    }
    // @JsonAnyGetter 用于序列化时将Map中的内容作为顶级字段输出
    // 注意:这里的实现是为了演示,实际序列化时可能需要更精细的控制
    @JsonAnyGetter
    public Map<String, List<Item>> getItems() {
        return items;
    }
    @Override
    public String toString() {
        return "Store{id=" + id + ", items=" + items + '}';
    }
}现在,我们可以使用 ObjectMapper 将整个JSON字符串反序列化为 List<Store>:
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class JsonDataBinder {
    private static final ObjectMapper MAPPER = new ObjectMapper();
    public List<Store> parseJsonToStores(String json) {
        try {
            return MAPPER.readValue(json, new TypeReference<List<Store>>() {});
        } catch (com.fasterxml.jackson.core.JsonProcessingException e) {
            System.err.println("Error parsing JSON to Store list: " + e.getMessage());
            return Collections.emptyList();
        }
    }
    public static void main(String[] args) {
        String json = "[\n" +
                "    {\n" +
                "        \"id\": 1,\n" +
                "        \"footwearList\": [\n" +
                "            { \"id\": 1, \"name\": \"sandals\", \"category\": \"men\" },\n" +
                "            { \"id\": 3, \"name\": \"sandals\", \"category\": \"women\" }\n" +
                "        ],\n" +
                "        \"clothingList\": [\n" +
                "            { \"id\": 1, \"name\": \"t-shirt\", \"category\": \"men\" },\n" +
                "            { \"id\": 3, \"name\": \"tshirt\", \"category\": \"women\" }\n" +
                "        ]\n" +
                "    },\n" +
                "    {\n" +
                "        \"id\": 2,\n" +
                "        \"footwearList\": [\n" +
                "            { \"id\": 2, \"name\": \"shoes\", \"category\": \"men\" },\n" +
                "            { \"id\": 4, \"name\": \"shoes\", \"category\": \"women\" }\n" +
                "        ],\n" +
                "        \"clothingList\": [\n" +
                "            { \"id\": 2, \"name\": \"shirt\", \"category\": \"men\" },\n" +
                "            { \"id\": 4, \"name\": \"shirt\", \"category\": \"women\" }\n" +
                "        ]\n" +
                "    }\n" +
                "]";
        JsonDataBinder binder = new JsonDataBinder();
        List<Store> stores = binder.parseJsonToStores(json);
        System.out.println("所有解析的Store对象:");
        stores.forEach(System.out::println);
        // 提取所有鞋类商品
        List<Item> allFootwear = stores.stream()
                                       .flatMap(store -> store.getItems().getOrDefault("footwearList", Collections.emptyList()).stream())
                                       .collect(Collectors.toList());
        System.out.println("\n所有鞋类商品:");
        allFootwear.forEach(System.out::println);
        // 提取所有男士服装
        List<Item> menClothing = stores.stream()
                                       .flatMap(store -> store.getItems().getOrDefault("clothingList", Collections.emptyList()).stream())
                                       .filter(item -> "men".equals(item.getCategory()))
                                       .collect(Collectors.toList());
        System.out.println("\n所有男士服装:");
        menClothing.forEach(System.out::println);
    }
}输出示例:
所有解析的Store对象:
Store{id=1, items={footwearList=[Item{id=1, name='sandals', category='men'}, Item{id=3, name='sandals', category='women'}], clothingList=[Item{id=1, name='t-shirt', category='men'}, Item{id=3, name='tshirt', category='women'}]}}
Store{id=2, items={footwearList=[Item{id=2, name='shoes', category='men'}, Item{id=4, name='shoes', category='women'}], clothingList=[Item{id=2, name='shirt', category='men'}, Item{id=4, name='shirt', category='women'}]}}
所有鞋类商品:
Item{id=1, name='sandals', category='men'}
Item{id=3, name='sandals', category='women'}
Item{id=2, name='shoes', category='men'}
Item{id=4, name='shoes', category='women'}
所有男士服装:
Item{id=1, name='t-shirt', category='men'}
Item{id=2, name='shirt', category='men'}Data Binding方法将整个JSON结构映射为强类型Java对象,使得后续的数据访问和操作(如使用Java Stream API进行过滤)变得非常直观和类型安全。
通过掌握Jackson Streaming API和Data Binding这两种方法,开发者可以根据具体需求,在Spring Boot中灵活高效地处理各种复杂嵌套的JSON数据。在大多数情况下,Data Binding结合 @JsonAnySetter 等注解,能够以最简洁的方式解决嵌套和动态键的问题。
以上就是Spring Boot中高效提取嵌套JSON数据的策略的详细内容,更多请关注php中文网其它相关文章!
 
                        
                        每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
 
                Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号