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

Spring中如何按需隐藏JSON字段?@ResponseBody隐藏@ResponseEntity恢复

解决Spring中同一对象在不同返回场景下动态隐藏/显示字段的问题

你遇到的这个场景其实很常见——同一个实体类,在不同接口返回时需要展示不同的字段,又不想重复创建DTO类。之前那些静态注解(比如@JsonIgnore、@JsonProperty(access=ReadOnly))之所以没生效,是因为它们都是全局生效的,没法根据返回方式(@ResponseBody/@ResponseEntity)动态切换。下面给你两种无需创建重复对象的解决方案,都是Jackson提供的动态序列化机制:

方法一:使用@JsonView(推荐,简洁直观)

这是Spring和Jackson集成时最常用的动态字段控制方式,核心是通过定义视图接口,标记哪些字段属于哪个视图,然后在接口方法上指定要使用的视图。

步骤1:定义视图接口

先创建两个视图接口,让“详情视图”继承“基础视图”,这样详情视图会自动包含基础视图的所有字段:

public class Views {
    // 基础视图:对应@ResponseBody返回时要显示的字段
    public interface Base {}
    // 详情视图:对应@ResponseEntity返回时要额外显示的字段
    public interface Detail extends Base {}
}

步骤2:在实体类上标记字段所属视图

在需要控制的字段(或getter方法)上添加@JsonView注解,指定它属于哪个视图:

public class User {
    @JsonView(Views.Base.class)
    private Long id;
    
    @JsonView(Views.Base.class)
    private String username;
    
    // 这个字段只在Detail视图下显示
    @JsonView(Views.Detail.class)
    private String password;

    // 省略getter、setter方法
}

步骤3:在Controller方法中指定视图

在返回@ResponseBody的方法上标记@JsonView(Views.Base.class),返回@ResponseEntity的方法上标记@JsonView(Views.Detail.class)即可:

@RestController
public class UserController {

    // 使用@ResponseBody(@RestController已默认包含),只显示Base视图的字段(隐藏password)
    @GetMapping("/user/base")
    @JsonView(Views.Base.class)
    public User getBaseUser() {
        User user = new User();
        user.setId(1L);
        user.setUsername("test");
        user.setPassword("123456");
        return user;
    }

    // 使用@ResponseEntity,显示Detail视图的字段(包含password)
    @GetMapping("/user/detail")
    @JsonView(Views.Detail.class)
    public ResponseEntity<User> getDetailUser() {
        User user = new User();
        user.setId(1L);
        user.setUsername("test");
        user.setPassword("123456");
        return ResponseEntity.ok(user);
    }
}

注意:如果是传统Spring MVC项目,需要确保MappingJackson2HttpMessageConverter的ObjectMapper开启了DEFAULT_VIEW_INCLUSION=false,这样只会序列化标记了对应视图的字段;Spring Boot项目默认已经配置好了,不用额外操作。

方法二:使用@JsonFilter(更灵活,适合动态场景)

如果你需要更灵活的控制(比如根据请求参数决定显示哪些字段),可以用Jackson的过滤器机制。

步骤1:给实体类添加过滤器标记

@JsonFilter("UserFieldFilter")
public class User {
    private Long id;
    private String username;
    private String password;

    // 省略getter、setter方法
}

步骤2:在Controller中动态设置过滤规则

通过MappingJacksonValue包装实体对象,动态指定要包含或排除的字段:

@RestController
public class UserController {

    // @ResponseBody返回时,排除password字段
    @GetMapping("/user/base")
    public MappingJacksonValue getBaseUser() {
        User user = new User();
        user.setId(1L);
        user.setUsername("test");
        user.setPassword("123456");

        // 创建过滤器:序列化所有字段,除了password
        SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.serializeAllExcept("password");
        FilterProvider filterProvider = new SimpleFilterProvider().addFilter("UserFieldFilter", filter);

        MappingJacksonValue result = new MappingJacksonValue(user);
        result.setFilters(filterProvider);
        return result;
    }

    // @ResponseEntity返回时,序列化所有字段(包含password)
    @GetMapping("/user/detail")
    public ResponseEntity<MappingJacksonValue> getDetailUser() {
        User user = new User();
        user.setId(1L);
        user.setUsername("test");
        user.setPassword("123456");

        // 创建过滤器:序列化所有字段
        SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.serializeAll();
        FilterProvider filterProvider = new SimpleFilterProvider().addFilter("UserFieldFilter", filter);

        MappingJacksonValue result = new MappingJacksonValue(user);
        result.setFilters(filterProvider);

        return ResponseEntity.ok(result);
    }
}

为什么之前的方法没生效?

  • @JsonIgnore:全局生效,一旦标记该字段,所有场景下都会被忽略,没法动态切换;
  • @JsonProperty(access=Access.READ_ONLY):只是控制字段的反序列化权限(不能从JSON转对象),但序列化时还是会一直显示;
  • @JsonIgnoreProperties:静态指定要忽略的字段,同样是全局生效,无法根据场景切换。

所以只有Jackson提供的动态视图/过滤机制,才能满足你同一对象在不同返回场景下显示不同字段的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:21:47