Java Spring Boot中实现不同结构JSON转换的方案咨询
可行解决方案:三种优雅的实体转响应JSON方案
针对你遇到的键名映射、嵌套结构转换问题,推荐以下几种比手动getter/setter更优的方案:
方案1:Jackson注解+响应POJO(最基础的优雅实现)
通过定义响应POJO并结合Jackson的@JsonProperty注解解决键名不一致问题,同时手动处理嵌套结构的包装逻辑(可配合Lombok简化代码)。
代码示例
定义响应DTO类
import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import java.util.List; @Data // Lombok注解,自动生成getter/setter、构造器等 public class ResponseDto { private String id; @JsonProperty("fullName") // 将name字段映射为fullName private String name; private String email; @JsonProperty("additionalInfo") private List<AdditionalInfo> additionalInfoList; @JsonProperty("academy") private List<AcademyItem> academyItems; @Data public static class AdditionalInfo { private String age; private String gender; private String nationality; private String language; } @Data public static class AcademyItem { @JsonProperty("score") // 将mark字段映射为score private String mark; } }
实体转DTO逻辑
// 假设已从数据库获取UserEntity实例 UserEntity userEntity = userRepository.findById("1").orElseThrow(); ResponseDto responseDto = new ResponseDto(); responseDto.setId(userEntity.getId()); responseDto.setName(userEntity.getName()); responseDto.setEmail(userEntity.getEmail()); // 包装additionalInfo为列表结构 ResponseDto.AdditionalInfo info = new ResponseDto.AdditionalInfo(); info.setAge(userEntity.getAge()); info.setGender(userEntity.getGender()); info.setNationality(userEntity.getNationality()); info.setLanguage(userEntity.getLanguage()); responseDto.setAdditionalInfoList(List.of(info)); // 转换academyDetails为academy列表 List<ResponseDto.AcademyItem> academyItems = userEntity.getAcademyDetails().stream() .map(detail -> { ResponseDto.AcademyItem item = new ResponseDto.AcademyItem(); item.setMark(detail.getMark()); return item; }) .collect(Collectors.toList()); responseDto.setAcademyItems(academyItems); // 序列化生成JSON ObjectMapper objectMapper = new ObjectMapper(); String responseJson = objectMapper.writeValueAsString(responseDto);
方案2:MapStruct自动映射框架(最优雅的生产级方案)
MapStruct是编译时生成映射代码的框架,性能优于反射,能自动处理字段映射,自定义复杂转换逻辑,完全替代手动getter/setter。
步骤1:添加依赖(Spring Boot项目)
<dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.5.5.Final</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.5.5.Final</version> <scope>provided</scope> </dependency>
步骤2:定义映射接口
import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Named; import java.util.List; import java.util.stream.Collectors; @Mapper(componentModel = "spring") // 让Spring管理该映射Bean public interface UserMapper { // 主实体转响应DTO,指定字段映射规则 @Mapping(source = "name", target = "fullName") @Mapping(source = ".", target = "additionalInfo", qualifiedByName = "buildAdditionalInfo") @Mapping(source = "academyDetails", target = "academy", qualifiedByName = "buildAcademyItems") ResponseDto entityToResponseDto(UserEntity userEntity); // 自定义转换:将UserEntity的多个字段包装为List<AdditionalInfo> @Named("buildAdditionalInfo") default List<ResponseDto.AdditionalInfo> buildAdditionalInfo(UserEntity entity) { ResponseDto.AdditionalInfo info = new ResponseDto.AdditionalInfo(); info.setAge(entity.getAge()); info.setGender(entity.getGender()); info.setNationality(entity.getNationality()); info.setLanguage(entity.getLanguage()); return List.of(info); } // 自定义转换:将AcademyDetailEntity转换为AcademyItem @Named("buildAcademyItems") default List<ResponseDto.AcademyItem> buildAcademyItems(List<AcademyDetailEntity> details) { return details.stream() .map(detail -> { ResponseDto.AcademyItem item = new ResponseDto.AcademyItem(); item.setScore(detail.getMark()); return item; }) .collect(Collectors.toList()); } }
步骤3:业务代码中使用
@Service public class UserService { private final UserRepository userRepository; private final UserMapper userMapper; // 构造注入 public UserService(UserRepository userRepository, UserMapper userMapper) { this.userRepository = userRepository; this.userMapper = userMapper; } public ResponseDto getUser(String id) { UserEntity user = userRepository.findById(id).orElseThrow(); return userMapper.entityToResponseDto(user); // 一行完成转换 } }
方案3:HashMap手动构建(适合简单临时场景)
如果不想定义POJO,可直接用HashMap构建响应结构,灵活但维护性较差,适合结构简单的场景。
代码示例
ObjectMapper objectMapper = new ObjectMapper(); UserEntity userEntity = userRepository.findById("1").orElseThrow(); // 构建响应Map Map<String, Object> responseMap = new HashMap<>(); responseMap.put("id", userEntity.getId()); responseMap.put("fullName", userEntity.getName()); responseMap.put("email", userEntity.getEmail()); // 构建additionalInfo Map<String, String> infoMap = new HashMap<>(); infoMap.put("age", userEntity.getAge()); infoMap.put("gender", userEntity.getGender()); infoMap.put("nationality", userEntity.getNationality()); infoMap.put("language", userEntity.getLanguage()); responseMap.put("additionalInfo", List.of(infoMap)); // 构建academy列表 List<Map<String, String>> academyList = userEntity.getAcademyDetails().stream() .map(detail -> { Map<String, String> item = new HashMap<>(); item.put("score", detail.getMark()); return item; }) .collect(Collectors.toList()); responseMap.put("academy", academyList); // 生成JSON String responseJson = objectMapper.writeValueAsString(responseMap);
内容的提问来源于stack exchange,提问作者iswarya
相关产品推荐
相关产品推荐

