当前位置: 首页 > 工具软件 > JSONView > 使用案例 >

@JsonView 详解(理论+实战)

刘运浩
2023-12-01

        @JsonView 对比场景

  • 数据库按需查询【推荐】

    Dao查询列表的时候,仅仅查询基础信息,不包含密码信息;查询详情的时候,就把更详细的详细查询并返回;

  • 定义不同的前端视图对象

    查询的时候,都把详细的查询出来,定义不同的响应对象并赋值返回,List<UserSimpleInfo>和UserDetailsInfo

  • 定义特定的对象转换工具

    业务对象到响应的视图对象转换时,定义一个特殊的转换工具类,根据需要,忽略掉对应的值,比如之前介绍的:【还用 BeanUtils 拷贝对象?MapStruct 才是王者!】就可以实现

  • @JsonView【推荐】

    同一个响应对象,通过指定不同的Json视图,来达到响应不同数据结构的目的

基础实现

创建一个用户对象

第一步:定义不同的视图对象

第二步:在属性的get方法上面指定不同的视图@JsonView(xxx.class);由于这里使用了Lombok,所以@JsonView注解直接添加在属性之上

package com.example.demo.pojo;

import com.example.demo.vo.BaseVO;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class User {
    // 名字
    @JsonView(UserSimpleView.class)
    private String userName;

    // 年龄
    @JsonView(UserSimpleView.class)
    private Integer age;

    // 密码
    @JsonView(UserDetailsView.class)
    private String pwd;

    // 基础的简单详细视图
    public interface UserSimpleView extends BaseVO.BaseResponceView {

    }

    // 详情视图
    // 详情视图继承自基础视图,意味着详情视图中包含了所有的基础视图数据
    public interface UserDetailsView extends UserSimpleView {
    }
}
package com.example.demo.vo;

import com.fasterxml.jackson.annotation.JsonView;
import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class BaseVO<T> {
    /**
     * 状态码
     */
    @JsonView(BaseResponceView.class)
    private Integer code;

    /**
     * 状态描述
     */
    @JsonView(BaseResponceView.class)
    private String msg;

    /**
     * 数据
     */
    @JsonView(BaseResponceView.class)
    private T data;



    // 基础的视图对象
    public interface BaseResponceView {
    }
}

测试用例

package com.example.demo;

import lombok.extern.slf4j.Slf4j;
import org.junit.Before;



import org.junit.Test;

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;


import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
@WebAppConfiguration
public class UserTests {
    @Autowired
    private WebApplicationContext webApplicationContext;

    private MockMvc mockMvc;
    @Before
    public void setup(){


        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    @Test
    public void list() throws Exception {
        String contentAsString = mockMvc.perform(MockMvcRequestBuilders.get("/user/list")
                .accept(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andReturn().getResponse().getContentAsString();
        log.info(contentAsString);
    }


    @Test
    public void userDetails() throws Exception{
        String contentAsString = mockMvc.perform(MockMvcRequestBuilders.get("/user/1")
                .accept(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andReturn().getResponse().getContentAsString();
        log.info(contentAsString);
    }

}

运行结果

2021-08-05 11:20:53.751  INFO 4404 --- [           main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
2021-08-05 11:20:53.751  INFO 4404 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : Initializing Servlet ''
2021-08-05 11:20:53.752  INFO 4404 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : Completed initialization in 1 ms
2021-08-05 11:20:53.817  INFO 4404 --- [           main] com.example.demo.UserTests               : {"code":0,"msg":"成功","data":[{"userName":"a","age":10},{"userName":"b","age":20},{"userName":"c","age":70}]}
2021-08-05 11:31:40.974  INFO 5780 --- [           main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
2021-08-05 11:31:40.974  INFO 5780 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : Initializing Servlet ''
2021-08-05 11:31:40.975  INFO 5780 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : Completed initialization in 1 ms
2021-08-05 11:31:41.044  INFO 5780 --- [           main] com.example.demo.UserTests               : {"code":0,"msg":"成功","data":{"userName":"a","age":10,"pwd":"123"}}
  • 业务对象的嵌套

    上面列举了基础结构嵌套业务数据的示例,实际的开发中同样存在多个业务对象间的嵌套,不同的场景,返回的嵌套对象不同,对于

    JsonView的配置也上面展示的基础结构配置没啥差异,举一反三即可实现了。

如何选择最好的方式

开头列举了几种不同的方式,来满足不同场景下返回不同数据结构的问题,并没有说哪一种就是最优的解决方案;需要根据不同的业务场景,来针对性选择;如果说单表的操作,可能直接通Dao层按需求查询对应的字段就能好了;如果业务逻辑比较复杂,最终数据来源于多个地方,通过数据库的方式会导致Dao越来越庞大,使用JsonView的方式可能很轻松就满足了需求;

我们最终目的是让结构更清晰,代码更合理,维护更容易,所以合适才是最重要。

注意

JsonView仅支持jackson框架;SpringBoot默认使用的框架就是jackson;如果你将Http的消息转换对象由jackson配置成了FastJson,那么所有的@JsonView配置将全部失效,所以务必注意;替换的具体配置如下:

@Bean
public HttpMessageConverters fastJsonHttpMessageConverters(){
    FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
    FastJsonConfig fastJsonConfig = new FastJsonConfig();
    fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue,SerializerFeature.WriteNullStringAsEmpty,SerializerFeature.PrettyFormat);
    fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
    List<MediaType> fastMediaTypes = new ArrayList<>();
    fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
    fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
    fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
    HttpMessageConverter<?> converter = fastJsonHttpMessageConverter;
    return new HttpMessageConverters(converter);
}

 类似资料: