JsonView和统一封装返回结果的问题

来源:3-3 用户详情请求

醒着长眠

2021-03-02

老师您好,项目里很多时候使用统一封装返回结果,例如课程中的SimpleResponse类。

public class User {
   public interface UserInfo{};
   public interface UserDetail extends UserInfo{};
   
   private String username;
   private String password;
   @JsonView(UserInfo.class)
   public String getUsername() {
       return username;
   }
   @JsonView(UserDetail.class)
   public String getPassword() {
       return password;
   }
}

@RequestMapping("/user")
@JsonView(User.UserInfo.class)
public User getUser(@RequestParam String username){
   User user = new User();
   user.setUsername("123");
   user.setPassword("123");
   return user;
}

@RequestMapping("/user")
@JsonView(User.UserInfo.class)
public SimpleResponse getUser(@RequestParam String username){
   User user = new User();
   user.setUsername("123");
   user.setPassword("123");
   return new SimpleResponse(user);
}

在第一个返回中 可以只给前端展示username属性

但是在第二个返回中 用SimpleResponse包装了User对象后,JsonView就失效了,在前端无法获取到User对象的数据。

想问下老师这种包装数据可不可以用@JsonView控制

谢谢老师

写回答

2回答

银色上弦月

2022-02-25

这个问题我也遇到了,我认为主要发生这个问题的原因在于:将原本的所要进行JsonView视图过滤的A对象置于B对象中。B类我称为包装类,而包装类没有进行JsonView设置。在Controller返回值进行序列化时,包装类没有匹配的JsonView,则全部将其过滤掉了,以至于没有数据返回。
解决思路有2个方向:

  1. 在包装类中添加JsonView注解。

  2. 重写包装类的序列化规则

第一个方向的思路

    如果包装类是本项目的类,那只要在包装类的对象中同样添加JsonView就可以了。

    如果包装类是其他框架引用的类,例如我所遇到的MyBatisPlus的分页对象Page,这种无法修改源码的情况,则考虑2种方法:

        1. 重写Page对象,将新对象添加JsonView注解,然后返回时将原Page对象复制到新Page对象。

        2. 通过反射,为Page对象动态添加JsonView注解。

这个思路我没有尝试下去,我认为这个思路虽然可行,但对代码的入侵性太大,不予考虑。主要尝试了第二种方案。

第二个方向的思路,重写Jackson对包装类的序列化规则。还是以MyBatisPlus的Page类为例

在不动原本代码的情况下,添加以下配置类:

@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder){
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Page.class, new PageJsonSerializer(objectMapper));
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }
}


public class PageJsonSerializer extends JsonSerializer<Page> {

    private ObjectMapper objectMapper;

    public PageJsonSerializer(ObjectMapper objectMapper){
        this.objectMapper = objectMapper;
    }

    @Override
    public void serialize(Page page, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
        gen.writeStartObject();
        gen.writeNumberField("total", page.getTotal());
        gen.writeNumberField("size", page.getSize());
        gen.writeNumberField("current", page.getCurrent());
        gen.writeNumberField("pages", page.getPages());
        gen.writeArrayFieldStart("records");
        objectMapper.writerWithView(serializerProvider.getActiveView()).writeValue(gen, page.getRecords());
        gen.writeEndArray();
        gen.writeEndObject();
    }
}

逻辑很简单,应该不用再解释了。主要思路就是告诉Jackson,当需要序列化Page这个对象时,除了records里按指定的JsonView序列化,其他字段原样进行序列化。

0
1
醒着长眠
看明白啦,十分感谢,解答的很详细
2022-04-25
共1条回复

JoJo

2021-04-14

额...每太看明白问题,你能处理SimpleResponse,想处理SimpleResponse类中包装的返回结果的话,直接get获取到处理就好了。

0
1
醒着长眠
老师不好意思没说明白,重新编辑了一下问题
2021-04-15
共1条回复

Spring Security技术栈开发企业级认证与授权

Spring Security技术栈,REST风格开发常见接口,独立开发认证授权模块保证REST服务安全

2662 学习 · 1561 问题

查看课程