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

如何使用Spring Boot实现JSON内嵌JSON结构的API响应

Spring Boot + Java 17 实现多层内嵌JSON接口返回方案

核心逻辑不需要手动拼接JSON字符串,利用Spring Boot默认集成的Jackson序列化能力,对齐结构定义Java对象+组装数据后直接返回即可,具体实现步骤如下:

1. 定义与目标JSON结构完全对齐的响应VO

不要直接把数据库实体类当做返回对象,单独定义纯响应用的VO层对象,字段名、层级和目标JSON一一对应:

  • 顶层根节点单独定义,包含所有顶层key对应的字段
  • 内嵌JSON对象直接定义对应类型的成员变量,内嵌JSON数组用集合类型声明
    示例代码:
// 顶层响应结构,对应JSON最外层的所有key
public record DbMockResponse(
    List<UserVO> users,
    List<PostVO> posts,
    List<CommentVO> comments
) {}

// 用户对象
public record UserVO(
    Long id,
    String name,
    String email
) {}

// 评论对象
public record CommentVO(
    Long id,
    String content,
    Long postId,
    Long userId
) {}

// 文章对象,包含内嵌的作者对象、评论数组
public record PostVO(
    Long id,
    String title,
    String body,
    UserVO author, // 对应内嵌JSON对象
    List<CommentVO> comments // 对应内嵌JSON数组
) {}

如果目标JSON的key是下划线命名(比如create_time),和Java驼峰命名不匹配,有两种处理方式:要么在字段上加@JsonProperty("create_time")指定序列化后的key名,要么全局配置Jackson的命名策略自动转换。

2. 数据查询与组装

因为数据存储在本地数据库,不要依赖ORM框架的级联查询硬做关联,用内存组装的方式性能更高、逻辑更可控:

  1. 先查询所有需要的基础表数据,把关联数据构建成ID映射的Map,避免循环查库
  2. 按照目标JSON的层级关系,把关联对象填充到对应VO的内嵌字段中
    示例服务层代码:
@Service
public class DbMockService {
    private final UserRepository userRepository;
    private final PostRepository postRepository;
    private final CommentRepository commentRepository;

    // 构造方法注入,无需写@Autowired
    public DbMockService(UserRepository userRepository, PostRepository postRepository, CommentRepository commentRepository) {
        this.userRepository = userRepository;
        this.postRepository = postRepository;
        this.commentRepository = commentRepository;
    }

    public DbMockResponse getAssembledResponse() {
        // 1. 全量查询基础表数据
        List<User> userDOList = userRepository.findAll();
        List<Post> postDOList = postRepository.findAll();
        List<Comment> commentDOList = commentRepository.findAll();

        // 2. 构建ID索引,方便快速匹配关联数据
        Map<Long, UserVO> userVOMap = userDOList.stream()
                .map(user -> new UserVO(user.getId(), user.getName(), user.getEmail()))
                .collect(Collectors.toMap(UserVO::id, Function.identity()));
        Map<Long, List<CommentVO>> postCommentMap = commentDOList.stream()
                .map(comment -> new CommentVO(comment.getId(), comment.getContent(), comment.getPostId(), comment.getUserId()))
                .collect(Collectors.groupingBy(CommentVO::postId));

        // 3. 组装文章的内嵌字段
        List<PostVO> postVOList = postDOList.stream()
                .map(post -> new PostVO(
                        post.getId(),
                        post.getTitle(),
                        post.getBody(),
                        // 填充内嵌作者对象
                        userVOMap.get(post.getAuthorId()),
                        // 填充当前文章对应的内嵌评论数组
                        postCommentMap.getOrDefault(post.getId(), Collections.emptyList())
                )).toList();

        // 4. 封装成顶层响应对象返回
        return new DbMockResponse(new ArrayList<>(userVOMap.values()), postVOList,
                commentDOList.stream()
                        .map(comment -> new CommentVO(comment.getId(), comment.getContent(), comment.getPostId(), comment.getUserId()))
                        .toList());
    }
}

3. 接口层直接返回组装好的对象

Spring Boot会自动通过Jackson把返回的Java对象序列化成JSON格式,多层嵌套的对象会自动转成内嵌JSON结构,不需要额外做序列化处理:

@RestController
public class DbMockController {
    private final DbMockService dbMockService;

    public DbMockController(DbMockService dbMockService) {
        this.dbMockService = dbMockService;
    }

    @GetMapping("/db")
    public DbMockResponse getDbJson() {
        return dbMockService.getAssembledResponse();
    }
}

常见配置优化(对齐示例结构)

在application.yml中添加以下配置,避免字段不匹配、空值多余的问题:

spring:
  jackson:
    # 全局自动把驼峰字段转成下划线格式序列化,适配常见JSON命名规范
    property-naming-strategy: SNAKE_CASE
    # 序列化时忽略null字段,避免返回多余的null值
    default-property-inclusion: non_null

避坑提醒

  • 不要手动拼接JSON字符串返回:手动拼接容易出现转义错误、格式异常,且返回字符串时Spring会把内容当做普通文本处理,不会按JSON格式解析
  • 避免循环引用:如果两个VO互相持有对方引用(比如UserVO里存List,PostVO里存UserVO),需要在其中一方的字段上加@JsonBackReference注解,避免序列化时出现栈溢出
  • 不要直接返回数据库实体:数据库实体通常和接口返回结构存在差异,直接返回容易出现字段泄露、结构不匹配的问题,单独定义VO层是最稳妥的做法

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 02:06:22