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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 10:18:10