0

0

使用Spring Boot和Jackson高效提取嵌套JSON数据

花韻仙語

花韻仙語

发布时间:2025-10-26 12:39:22

|

587人浏览过

|

来源于php中文网

原创

使用spring boot和jackson高效提取嵌套json数据

本文深入探讨了在Spring Boot应用中利用Jackson库处理复杂嵌套JSON数据的两种核心策略:数据绑定(Data Binding)和流式API(Streaming API/Tree Model)。文章通过具体代码示例,详细阐述了如何将嵌套JSON映射到Java对象,以及如何在结构未知或大型JSON场景下通过遍历JsonNode来提取特定信息,并提供了筛选分类数据的实现方法,旨在帮助开发者根据实际需求选择最合适的JSON处理方案。

1. 引言

在现代微服务架构中,Spring Boot应用程序经常需要与外部API进行交互,处理和解析复杂的JSON响应是常见的任务。当JSON数据包含多层嵌套结构时,如何高效、健壮地提取所需信息成为一个挑战。本文将重点介绍如何利用Jackson库,在Spring Boot环境中优雅地处理这类嵌套JSON数据,并提供两种主要方法:数据绑定和流式API(或称为树模型)。

2. 准备工作:Jackson依赖

Jackson是Spring Boot默认集成的JSON处理库,通常无需额外添加依赖。但如果项目是手动构建或需要特定版本,请确保pom.xml中包含以下Jackson相关依赖:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.4</version> <!-- 请使用最新稳定版本 -->
</dependency>

3. 方法一:Jackson数据绑定 (Data Binding)

对于结构相对固定且已知,或者数据量适中的JSON,数据绑定是首选方案。它将JSON直接映射到预定义的Java对象(POJO),提供了极佳的可读性和类型安全性。

3.1 定义POJO类

首先,根据JSON结构定义对应的Java类。对于给定的JSON示例:

[
    {
        "id": 1,
        "footwearList": [...],
        "clothingList": [...]
    },
    {
        "id": 2,
        "footwearList": [...],
        "clothingList": [...]
    }
]

其中footwearList和clothingList内部结构相同,可以抽象为一个Item类。而外部对象包含一个id和多个动态列表。

Item类:

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 + '\'' +
               '}';
    }
}

Store类 (处理动态列表键):

为了处理footwearList和clothingList这种键名不固定但值类型一致的结构,可以使用@JsonAnySetter和@JsonAnyGetter将它们映射到一个Map<String, List<Item>>中。

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) {
        items.put(key, value);
    }

    // 使用 @JsonAnyGetter 序列化回JSON(可选,取决于需求)
    @JsonAnyGetter
    public Map<String, List<Item>> getItems() {
        return items;
    }

    @Override
    public String toString() {
        return "Store{" +
               "id=" + id +
               ", items=" + items +
               '}';
    }
}

3.2 反序列化JSON

有了POJO类,就可以使用ObjectMapper进行反序列化。

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import java.util.stream.Collectors;

public class JsonDataBindingService {

    private final ObjectMapper objectMapper = new ObjectMapper();

    public List<Store> parseStores(String jsonString) throws Exception {
        // 反序列化JSON字符串为List<Store>
        return objectMapper.readValue(jsonString, new TypeReference<List<Store>>() {});
    }

    public List<Item> getFootwearItemsByCategory(List<Store> stores, String category) {
        return stores.stream()
                     .flatMap(store -> store.getItems().getOrDefault("footwearList", List.of()).stream())
                     .filter(item -> item.getCategory().equalsIgnoreCase(category))
                     .collect(Collectors.toList());
    }

    public List<Item> getClothingItemsByCategory(List<Store> stores, String category) {
        return stores.stream()
                     .flatMap(store -> store.getItems().getOrDefault("clothingList", List.of()).stream())
                     .filter(item -> item.getCategory().equalsIgnoreCase(category))
                     .collect(Collectors.toList());
    }

    public static void main(String[] args) throws Exception {
        String json = "[{\"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\"}]}]";

        JsonDataBindingService service = new JsonDataBindingService();
        List<Store> stores = service.parseStores(json);

        System.out.println("所有商店数据:");
        stores.forEach(System.out::println);

        // 提取所有男士鞋类
        List<Item> menFootwear = service.getFootwearItemsByCategory(stores, "men");
        System.out.println("\n所有男士鞋类:");
        menFootwear.forEach(System.out::println);

        // 提取所有女士服装
        List<Item> womenClothing = service.getClothingItemsByCategory(stores, "women");
        System.out.println("\n所有女士服装:");
        womenClothing.forEach(System.out::println);
    }
}

优点:

歌者PPT
歌者PPT

歌者PPT,AI 写 PPT 永久免费

下载
  • 类型安全: 操作的是强类型Java对象,减少运行时错误。
  • 可读性高: 代码更简洁,易于理解和维护。
  • 开发效率: 一旦POJO定义完成,JSON处理变得非常简单。

注意事项:

  • 适用于JSON结构相对稳定且可预测的场景。
  • 对于非常大的JSON文件,一次性加载到内存可能导致内存消耗过高。

4. 方法二:Jackson流式API / 树模型 (Streaming API / Tree Model)

当JSON结构复杂、未知,或者需要处理超大型JSON文件以优化内存使用时,Jackson的流式API或树模型提供了更大的灵活性。树模型通过JsonNode表示JSON结构,允许开发者以类似DOM的方式遍历和查询数据。

4.1 使用JsonNode遍历

首先,将JSON字符串解析为JsonNode树。然后,可以遍历这棵树来查找特定的字段。

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.List;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.StreamSupport;

public class JsonTreeModelService {

    private final ObjectMapper objectMapper = new ObjectMapper();

    // 用于匹配包含 "footwear" 的键
    public static final Predicate<String> FOOTWEAR_PREDICATE = Pattern.compile("footwear", Pattern.CASE_INSENSITIVE).asPredicate();
    // 用于匹配包含 "clothing" 的键
    public static final Predicate<String> CLOTHING_PREDICATE = Pattern.compile("clothing", Pattern.CASE_INSENSITIVE).asPredicate();

    /**
     * 将JsonNode转换为Item对象
     */
    private Item nodeToItem(JsonNode node) {
        // 假设每个Item节点都有id, name, category字段
        int id = node.has("id") ? node.get("id").asInt() : 0;
        String name = node.has("name") ? node.get("name").asText() : null;
        String category = node.has("category") ? node.get("category").asText() : null;
        return new Item(id, name, category);
    }

    /**
     * 从JSON字符串中提取特定列表(如footwearList, clothingList)中的Item对象
     * 并可选择按category过滤。
     *
     * @param jsonString 原始JSON字符串
     * @param keyPredicate 用于匹配列表键的谓词 (例如 FOOTWEAR_PREDICATE, CLOTHING_PREDICATE)
     * @param filterCategory 可选的分类过滤字符串,如果为null或空则不进行过滤
     * @return 匹配的Item列表
     * @throws Exception 如果JSON解析失败
     */
    public List<Item> extractItemsFromNestedLists(String jsonString, Predicate<String> keyPredicate, String filterCategory) throws Exception {
        JsonNode rootNode = objectMapper.readTree(jsonString);
        List<Item> extractedItems = new ArrayList<>();

        if (rootNode.isArray()) {
            for (JsonNode storeNode : rootNode) {
                // 遍历Store节点下的所有字段
                storeNode.fields().forEachRemaining(entry -> {
                    String fieldName = entry.getKey();
                    JsonNode fieldNode = entry.getValue();

                    if (keyPredicate.test(fieldName) && fieldNode.isArray()) {
                        for (JsonNode itemNode : fieldNode) {
                            Item item = nodeToItem(itemNode);
                            // 应用分类过滤
                            if (filterCategory == null || filterCategory.isEmpty() ||
                                item.getCategory().equalsIgnoreCase(filterCategory)) {
                                extractedItems.add(item);
                            }
                        }
                    }
                });
            }
        }
        return extractedItems;
    }

    public static void main(String[] args) throws Exception {
        String json = "[{\"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\"}]}]";

        JsonTreeModelService service = new JsonTreeModelService();

        // 提取所有鞋类 (不按分类过滤)
        List<Item> allFootwear = service.extractItemsFromNestedLists(json, FOOTWEAR_PREDICATE, null);
        System.out.println("所有鞋类:");
        allFootwear.forEach(System.out::println);

        // 提取所有女士服装
        List<Item> womenClothing = service.extractItemsFromNestedLists(json, CLOTHING_PREDICATE, "women");
        System.out.println("\n所有女士服装:");
        womenClothing.forEach(System.out::println);

        // 提取所有男士鞋类
        List<Item> menFootwear = service.extractItemsFromNestedLists(json, FOOTWEAR_PREDICATE, "men");
        System.out.println("\n所有男士鞋类:");
        menFootwear.forEach(System.out::println);
    }
}

优点:

  • 灵活性高: 无需预先定义所有POJO,适用于JSON结构不确定或频繁变化的场景。
  • 内存效率: 可以按需读取和处理JSON节点,避免一次性加载整个大文件到内存。
  • 强大的查询能力: 结合JsonNode的各种方法(get(), path(), elements(), fields()等),可以实现复杂的JSON查询。

注意事项:

  • 代码相对更冗长,需要手动处理类型转换和空值检查。
  • 可读性不如数据绑定。

5. 总结与选择建议

  • 数据绑定 (Data Binding):

    • 推荐场景: JSON结构稳定、已知,数据量适中,追求代码简洁、类型安全和开发效率。这是大多数日常开发任务的首选。
    • 优点: 易于理解和维护,自动类型转换,减少样板代码。
    • 缺点: 对于结构高度动态或超大型JSON文件可能不适用。
  • 流式API / 树模型 (Streaming API / Tree Model):

    • 推荐场景: JSON结构高度动态、未知,或者需要处理内存中无法完全容纳的超大型JSON文件,对性能和内存使用有严格要求。
    • 优点: 极高的灵活性,内存效率高,适用于复杂查询。
    • 缺点: 代码相对复杂,需要手动处理数据类型和空值,可读性较低。

在Spring Boot应用中,通常会优先考虑使用Jackson的数据绑定功能,因为它能带来更高的开发效率和代码可维护性。只有在遇到数据绑定难以处理的特殊情况(如JSON结构极度不规则或文件过大)时,才考虑使用更底层的流式API或树模型。

通过以上两种方法,开发者可以根据具体需求,灵活选择Jackson提供的强大功能,高效地在Spring Boot应用中处理各种嵌套JSON数据。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
spring框架介绍
spring框架介绍

本专题整合了spring框架相关内容,想了解更多详细内容,请阅读专题下面的文章。

160

2025.08.06

Java Spring Security 与认证授权
Java Spring Security 与认证授权

本专题系统讲解 Java Spring Security 框架在认证与授权中的应用,涵盖用户身份验证、权限控制、JWT与OAuth2实现、跨站请求伪造(CSRF)防护、会话管理与安全漏洞防范。通过实际项目案例,帮助学习者掌握如何 使用 Spring Security 实现高安全性认证与授权机制,提升 Web 应用的安全性与用户数据保护。

88

2026.01.26

spring boot框架优点
spring boot框架优点

spring boot框架的优点有简化配置、快速开发、内嵌服务器、微服务支持、自动化测试和生态系统支持。本专题为大家提供spring boot相关的文章、下载、课程内容,供大家免费下载体验。

139

2023.09.05

spring框架有哪些
spring框架有哪些

spring框架有Spring Core、Spring MVC、Spring Data、Spring Security、Spring AOP和Spring Boot。详细介绍:1、Spring Core,通过将对象的创建和依赖关系的管理交给容器来实现,从而降低了组件之间的耦合度;2、Spring MVC,提供基于模型-视图-控制器的架构,用于开发灵活和可扩展的Web应用程序等。

408

2023.10.12

Java Spring Boot开发
Java Spring Boot开发

本专题围绕 Java 主流开发框架 Spring Boot 展开,系统讲解依赖注入、配置管理、数据访问、RESTful API、微服务架构与安全认证等核心知识,并通过电商平台、博客系统与企业管理系统等项目实战,帮助学员掌握使用 Spring Boot 快速开发高效、稳定的企业级应用。

73

2025.08.19

Java Spring Boot 4更新教程_Java Spring Boot 4有哪些新特性
Java Spring Boot 4更新教程_Java Spring Boot 4有哪些新特性

Spring Boot 是一个基于 Spring 框架的 Java 开发框架,它通过 约定优于配置的原则,大幅简化了 Spring 应用的初始搭建、配置和开发过程,让开发者可以快速构建独立的、生产级别的 Spring 应用,无需繁琐的样板配置,通常集成嵌入式服务器(如 Tomcat),提供“开箱即用”的体验,是构建微服务和 Web 应用的流行工具。

150

2025.12.22

Java Spring Boot 微服务实战
Java Spring Boot 微服务实战

本专题深入讲解 Java Spring Boot 在微服务架构中的应用,内容涵盖服务注册与发现、REST API开发、配置中心、负载均衡、熔断与限流、日志与监控。通过实际项目案例(如电商订单系统),帮助开发者掌握 从单体应用迁移到高可用微服务系统的完整流程与实战能力。

271

2025.12.24

Spring Boot企业级开发与MyBatis Plus实战
Spring Boot企业级开发与MyBatis Plus实战

本专题面向 Java 后端开发者,系统讲解如何基于 Spring Boot 与 MyBatis Plus 构建高效、规范的企业级应用。内容涵盖项目架构设计、数据访问层封装、通用 CRUD 实现、分页与条件查询、代码生成器以及常见性能优化方案。通过完整实战案例,帮助开发者提升后端开发效率,减少重复代码,快速交付稳定可维护的业务系统。

33

2026.02.11

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.4万人学习

C# 教程
C# 教程

共94课时 | 11.3万人学习

Java 教程
Java 教程

共578课时 | 81.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号