SpringBoot对象JSON扁平化教程
编程并不是一个机械性的工作,而是需要有思考,有创新的工作,语法是固定的,但解决问题的思路则是依靠人的思维,这就需要我们坚持学习和更新自己的知识。今天golang学习网就整理分享《Spring Boot单值对象JSON扁平化教程》,文章讲解的知识点主要包括,如果你对文章方面的知识点感兴趣,就不要错过golang学习网,在这可以对大家的知识积累有所帮助,助力开发能力的提升。

本文旨在解决Spring Boot应用中,将包含单值Java对象的实体序列化为JSON时,出现嵌套结构而非扁平化字符串的问题。通过引入数据传输对象(DTO)模式,我们将详细演示如何重构数据模型和API响应,以实现更简洁、符合预期的JSON输出格式,同时提升API设计的灵活性与安全性。
在构建基于Spring Boot和Hibernate的RESTful服务时,我们经常会遇到将领域模型对象转换为JSON响应的需求。然而,当领域模型中包含一些本质上是“单值对象”(Value Object),例如包装了字符串的EmailAddress类时,默认的JSON序列化行为可能会导致不必要的嵌套结构,这与我们期望的扁平化表示不符。
问题场景描述
假设我们有一个EmailAddress类,它封装了一个电子邮件地址的字符串值以及一些相关业务逻辑(如获取域名、邮箱等)。
// EmailAddress.java
public class EmailAddress {
public String value; // 实际的邮箱地址字符串
public EmailAddress(String value) {
this.value = value;
}
// 假设还有其他业务方法,如tld(), host(), mailbox()等
public String tld() { /* ... */ return "com"; }
public String host() { /* ... */ return "example.com"; }
public String mailbox() { /* ... */ return "user"; }
// 为了方便演示,添加getter
public String getValue() {
return value;
}
}接着,这个EmailAddress类被用作Customer实体的一个字段:
// Customer.java
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private EmailAddress mail; // 使用EmailAddress对象
// 构造函数、getter/setter略
public Customer() {}
public Customer(String name, EmailAddress mail) {
this.name = name;
this.mail = mail;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public EmailAddress getMail() { return mail; }
public void setMail(EmailAddress mail) { this.mail = mail; }
}当通过Spring Boot的REST服务暴露Customer实体时,默认的Jackson序列化器会将其中的EmailAddress对象视为一个普通的POJO,并将其内部的value字段也序列化出来,导致生成如下的JSON结构:
{
"id": 1,
"name": "Test",
"mail": {
"value": "test@example.com"
}
}然而,我们期望的输出是更简洁的扁平化结构,直接将mail字段表示为字符串:
{
"id": 1,
"name": "Test",
"mail": "test@example.com"
}解决方案:引入数据传输对象(DTO)
为了解决这个问题,最推荐且最灵活的方法是引入数据传输对象(DTO)。DTO是一种设计模式,用于在应用程序的不同层之间传输数据。它将API响应与内部领域模型解耦,允许我们精确控制API的输出格式。
1. 定义DTO类
首先,为我们的API响应定义一个CustomerDTO类。在这个DTO中,我们将EmailAddress字段扁平化为一个简单的String类型。
// CustomerDTO.java
public class CustomerDTO {
private Long id;
private String name;
private String email; // 将EmailAddress扁平化为String
// 构造函数、getter/setter
public CustomerDTO() {}
public CustomerDTO(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}2. 实现实体到DTO的映射
接下来,我们需要在服务层或控制器层实现从Customer实体到CustomerDTO的映射逻辑。这可以通过手动创建DTO实例、使用构造函数、或者利用专门的映射库(如ModelMapper、MapStruct)来完成。
手动映射示例:
// CustomerService.java (示例服务层)
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class CustomerService {
// 假设这里有CustomerRepository或其他数据访问层
// private final CustomerRepository customerRepository;
// 构造函数注入略
public CustomerDTO getCustomerById(Long id) {
// 假设从数据库获取Customer实体
Customer customer = new Customer(
"Test", new EmailAddress("test@example.com")
); // 模拟从数据库获取
customer.setId(id);
return mapToDTO(customer);
}
public List<CustomerDTO> getAllCustomers() {
// 假设从数据库获取所有Customer实体
List<Customer> customers = List.of(
new Customer("Alice", new EmailAddress("alice@example.com")),
new Customer("Bob", new EmailAddress("bob@example.com"))
);
customers.get(0).setId(1L);
customers.get(1).setId(2L);
return customers.stream()
.map(this::mapToDTO)
.collect(Collectors.toList());
}
private CustomerDTO mapToDTO(Customer customer) {
if (customer == null) {
return null;
}
return new CustomerDTO(
customer.getId(),
customer.getName(),
customer.getMail() != null ? customer.getMail().getValue() : null
);
}
// 如果需要处理传入的DTO,也需要实现DTO到实体的映射
public Customer saveCustomer(CustomerDTO customerDTO) {
Customer customer = mapToEntity(customerDTO);
// 保存customer到数据库
return customer;
}
private Customer mapToEntity(CustomerDTO customerDTO) {
if (customerDTO == null) {
return null;
}
return new Customer(
customerDTO.getName(),
customerDTO.getEmail() != null ? new EmailAddress(customerDTO.getEmail()) : null
);
}
}3. 更新REST控制器
最后,修改您的REST控制器,使其返回CustomerDTO对象而不是Customer实体。
// CustomerController.java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/customers")
public class CustomerController {
private final CustomerService customerService;
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
@GetMapping("/{id}")
public CustomerDTO getCustomer(@PathVariable Long id) {
return customerService.getCustomerById(id);
}
@GetMapping
public List<CustomerDTO> getAllCustomers() {
return customerService.getAllCustomers();
}
}现在,当您访问/api/customers/1时,将会得到期望的扁平化JSON响应:
{
"id": 1,
"name": "Test",
"email": "test@example.com"
}注意事项与最佳实践
映射库的选择: 对于复杂的实体和DTO映射,手动编写映射代码会变得繁琐且容易出错。推荐使用成熟的映射库:
- ModelMapper: 运行时反射映射,配置简单,但性能略低于编译时映射。
- MapStruct: 编译时代码生成,性能优异,但需要更多配置。
DTO的职责: DTO应专注于数据传输,不包含业务逻辑。它通常只包含字段、构造函数、getter和setter。
双向映射: 在处理POST/PUT请求时,您可能还需要实现从DTO到实体的映射。确保在映射过程中处理好可能的空值和数据转换。
数据验证: 可以在DTO上使用JSR 303/349 (Bean Validation) 注解(如@NotBlank, @Email等)来对传入的数据进行验证,从而将验证逻辑与领域模型分离。
安全性: 使用DTO可以避免在API响应中暴露敏感的内部实体字段,从而提高安全性。
@JsonValue注解(替代方案): 如果EmailAddress对象在任何JSON序列化场景下都应该被表示为一个简单的字符串,那么可以直接在EmailAddress类上使用@JsonValue注解。
// EmailAddress.java import com.fasterxml.jackson.annotation.JsonValue; public class EmailAddress { public String value; public EmailAddress(String value) { this.value = value; } @JsonValue // 标记此方法返回的值作为JSON序列化的内容 public String getValue() { return value; } // 其他方法... }这种方式更简洁,但不如DTO灵活,因为它强制了EmailAddress的单一JSON表示。如果EmailAddress在不同上下文需要不同的JSON表示,DTO是更好的选择。
总结
通过采用数据传输对象(DTO)模式,我们能够有效地解决Spring Boot中单值对象默认JSON序列化为嵌套结构的问题。DTO不仅能帮助我们实现扁平化的JSON输出,还带来了API与领域模型解耦、增强数据验证、提升安全性和灵活性的诸多优势。在实际开发中,结合映射库的使用,可以进一步提高开发效率和代码质量。
今天关于《SpringBoot对象JSON扁平化教程》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于的内容请关注golang学习网公众号!
Fishbowl鱼缸跑分测试及免费地址分享
- 上一篇
- Fishbowl鱼缸跑分测试及免费地址分享
- 下一篇
- Pythonfor循环去重求和方法
-
- 文章 · java教程 | 1分钟前 |
- Java命令行待办系统开发教程
- 439浏览 收藏
-
- 文章 · java教程 | 34分钟前 |
- Java可重入自旋锁实现全解析
- 426浏览 收藏
-
- 文章 · java教程 | 36分钟前 | java
- Java高效栈队列:ArrayDeque使用技巧
- 232浏览 收藏
-
- 文章 · java教程 | 54分钟前 |
- Java项目部署到Tomcat的完整教程
- 318浏览 收藏
-
- 文章 · java教程 | 57分钟前 |
- Java环境搭建快速上手指南
- 334浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java格式化输出技巧全解析
- 187浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java虚拟线程与Go并发性能对比实测
- 151浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java控制台投票系统开发教程
- 319浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java实现文件在线预览技巧解析
- 430浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Javaequals为何要重写?对象比较全解析
- 196浏览 收藏
-
- 文章 · java教程 | 2小时前 |
- JavaSimpleDateFormat使用技巧解析
- 460浏览 收藏
-
- 文章 · java教程 | 2小时前 |
- 强引用与弱引用区别解析
- 493浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ChatExcel酷表
- ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
- 3448次使用
-
- Any绘本
- 探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
- 3648次使用
-
- 可赞AI
- 可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
- 3680次使用
-
- 星月写作
- 星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
- 4816次使用
-
- MagicLight
- MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
- 4045次使用
-
- 提升Java功能开发效率的有力工具:微服务架构
- 2023-10-06 501浏览
-
- 掌握Java海康SDK二次开发的必备技巧
- 2023-10-01 501浏览
-
- 如何使用java实现桶排序算法
- 2023-10-03 501浏览
-
- Java开发实战经验:如何优化开发逻辑
- 2023-10-31 501浏览
-
- 如何使用Java中的Math.max()方法比较两个数的大小?
- 2023-11-18 501浏览

