优雅编程 - HttpMessageConverter

消息转换器

在Spring中org.springframework.http.converter.HttpMessageConverter规范中定义了Http请求和响应的消息转换规范, 我们知道SpringMvc可以接收不同的消息形式,也可以将不同的消息形式响应回去(最常见的是json);这些消息所蕴含的"有效信息"是一致的,那么各种不同的消息转换器,都会生成同样的转换结果. 至于各种消息间解析细节的不同,就被屏蔽在不同的HttpMessageConverter实现类中.

SpringMVC中使用FastJson作为转换器

通过SpringMvc中message-converts配置FastJson作为转换器

<!-- 默认的注解映射的支持,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping -->
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
   <mvc:message-converters register-defaults="true">
      <!-- 将Jackson2HttpMessageConverter的默认格式化输出为true -->
      <!-- 配置Fastjson支持 -->
      <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
         <property name="supportedMediaTypes">
            <list>
               <value>text/html;charset=UTF-8</value>
               <value>application/json</value>
            </list>
         </property>
         <property name="features">
            <list>
               <!-- 输出key时是否使用双引号 -->
               <value>QuoteFieldNames</value>
               <!-- 是否输出值为null的字段 -->
               <!-- <value>WriteMapNullValue</value> -->
               <!-- 数值字段如果为null,输出为0,而非null -->
               <value>WriteNullNumberAsZero</value>
               <!-- List字段如果为null,输出为[],而非null -->
               <value>WriteNullListAsEmpty</value>
               <!-- 字符类型字段如果为null,输出为"",而非null -->
               <value>WriteNullStringAsEmpty</value>
               <!-- Boolean字段如果为null,输出为false,而非null -->
               <value>WriteNullBooleanAsFalse</value>
               <!-- null String不输出  -->
               <value>WriteNullStringAsEmpty</value>
               <!-- null String也要输出  -->
               <!-- <value>WriteMapNullValue</value> -->
           
               <!-- Date的日期转换器 -->
               <value>WriteDateUseDateFormat</value>
            </list>
         </property>
      </bean>
   </mvc:message-converters>
</mvc:annotation-driven>

<!-- REST中根据URL后缀自动判定Content-Type及相应的View -->
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
   <property name="mediaTypes" >
      <map>
         <entry key="json" value="application/json"/>
      </map>
   </property>
   <!-- 这里是否忽略掉accept header,默认就是false -->
   <property name="ignoreAcceptHeader" value="true"/>
   <property name="favorPathExtension" value="true"/>
</bean>
复制代码

Fastjson轻量级属性转换

定义示例对象模型

做fastjson轻量注解配置, 更多配置参考github.com/alibaba/fas…

public class ModelTest implements Serializable{

    // 使用ordinal指定字段
    @JSONField(ordinal = 1)
    private Long id;

    @JSONField(ordinal = 2)
    private String name;

    @JSONField(ordinal = 3)
    private Integer age;

    // 使用serialize/deserialize指定字段不序列化
    @JSONField(deserialize = false, serialize = false)
    private String remark;

    // 配置date序列化和反序列使用yyyyMMdd日期格式
    @JSONField(format="yyyy-MM-dd HH:mm:ss")
    private Date crateTime;

    // 配置属性序列化使用的名称
    @JSONField(name = "updateTime", format="yyyy-MM-dd HH:mm:ss")
    private Date modifyTime;

    @JSONField(ordinal = 0)
    private DeleteEnum deleteEnum;

    public enum DeleteEnum {

        DISABLE(1, "禁用"),
        ENABLE(2, "启用");

        private int value;
        private String depict;

        DeleteEnum(int value, String depict) {
            this.value = value;
            this.depict = depict;
        }

        public static DeleteEnum findByValue(int value) {
            switch (value) {
                case 1:
                    return DISABLE;
                case 2:
                    return ENABLE;
                default:
                    return null;
            }
        }
    }

// getter and setter
}

复制代码

定义示例Controller

示例完成序列化数据到前端和提交JSON数据转换成对象模型

@Controller
@RequestMapping(value = "/test")
public class TestWebController {

    private static final Logger LOG = LoggerFactory.getLogger(TestWebController.class);
	
	  // 序列化对象到视图
    @ResponseBody
    @RequestMapping(value = {"/bean/data"}, method = {RequestMethod.GET, RequestMethod.POST})
    public Object toBody(){

        ModelTest modelTest = new ModelTest();
        modelTest.setAge(11);
        modelTest.setCrateTime(new Date());
        modelTest.setModifyTime(new Date());
        modelTest.setId(1L);
        modelTest.setName("测试Fastjson");
        modelTest.setRemark("备注");
        modelTest.setDeleteEnum(ModelTest.DeleteEnum.ENABLE);

        return ImmutableMap.<String, String>builder()
                .put("code", "0")
                .put("data", JSON.toJSONString(modelTest)).build();
    }

    // 解析JSON对象到实体模型
    @ResponseBody
    @RequestMapping(value = {"/bean/put"}, method = {RequestMethod.GET, RequestMethod.POST})
    public Object getBody(@RequestBody ModelTest modelTest){
        System.out.println(JSON.toJSONString(modelTest));
        return ImmutableMap.<String, String>builder()
                .put("code", "0")
                .put("data", "ok").build();
    }
}
复制代码

测试对象序列化到前端展示

@Test
    public void fastJSONTest01() throws Exception {
        MvcResult mvcResult = this.mockMvc.perform(MockMvcRequestBuilders.post("/test/bean/data")
                .cookie(new Cookie("token", "F3AF5F1D14F534F677XF3A00E352C"))   // 登录授权
                .accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
                .andExpect(handler().handlerType(TestWebController.class))      // 验证执行的控制器类型
                .andExpect(status().isOk())                                     // 验证执行状态
                .andDo(print())                                                 // 打印交互信息
                .andReturn();
        System.out.println(mvcResult.getResponse().getContentAsString());
    }
复制代码

执行结果:

{
  "code": "0",
  "data": "{"crateTime":"2016-12-2808: 02: 50","deleteEnum":"ENABLE","updateTime":"2016-12-2808: 02: 50","id":1,"name":"测试Fastjson","age":11}"
}
复制代码

测试提交数据转换成模型

@Test
    public void fastJSONTest02() throws Exception {

        ModelTest modelTest = new ModelTest();
        modelTest.setAge(11);
        modelTest.setCrateTime(new Date());
        modelTest.setModifyTime(new Date());
        modelTest.setId(1L);
        modelTest.setName("测试Fastjson");
        modelTest.setRemark("备注");
        modelTest.setDeleteEnum(ModelTest.DeleteEnum.ENABLE);
    
        MvcResult mvcResult = this.mockMvc.perform(MockMvcRequestBuilders.post("/test/bean/put")
                .content(JSON.toJSONString(modelTest))
                .contentType(MediaType.APPLICATION_JSON)
                .cookie(new Cookie("token", "F3AF5F1D14F534F677XF3A00E352C"))   // 登录授权
                .accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
                .andExpect(handler().handlerType(TestWebController.class))      // 验证执行的控制器类型
                .andExpect(status().isOk())                                     // 验证执行状态
                .andDo(print())                                                 // 打印交互信息
                .andReturn();
        System.out.println(mvcResult.getResponse().getContentAsString());
    }
复制代码

执行后台打印:

{"crateTime":"2016-12-28 08:04:33","deleteEnum":"ENABLE","updateTime":"2016-12-28 08:04:33","id":1,"name":"测试Fastjson","age":11}

复制代码

枚举绑定处理

示例中我们看到序列化的枚举为枚举的name(). 如果我们想用枚举的寓意值进行传输过程中的映射时,可以这样做, 透传一个数值, 该数值和枚举在getter()和setter()方法上绑定即可.

修改示例的对象模型

public class ModelTest implements Serializable{

    // 使用ordinal指定字段
    @JSONField(ordinal = 1)
    private Long id;

    @JSONField(ordinal = 2)
    private String name;

    @JSONField(ordinal = 3)
    private Integer age;

    // 使用serialize/deserialize指定字段不序列化
    @JSONField(deserialize = false, serialize = false)
    private String remark;

    // 配置date序列化和反序列使用yyyyMMdd日期格式
    @JSONField(format="yyyy-MM-dd HH:mm:ss")
    private Date crateTime;

    // 配置属性序列化使用的名称
    @JSONField(name = "updateTime", format="yyyy-MM-dd HH:mm:ss")
    private Date modifyTime;

    @JSONField(ordinal = 0)
    private DeleteEnum deleteEnum;

    private int enable;

    public enum DeleteEnum {

        DISABLE(1, "禁用"),
        ENABLE(2, "启用");

        private int value;
        private String depict;

        DeleteEnum(int value, String depict) {
            this.value = value;
            this.depict = depict;
        }

        public static DeleteEnum findByValue(int value) {
            switch (value) {
                case 1:
                    return DISABLE;
                case 2:
                    return ENABLE;
                default:
                    return null;
            }
        }
    }
	
	 // 数值绑定枚举
    public int getEnable() {
        this.deleteEnum = DeleteEnum.findByValue(enable);
        return enable;
    }
	 
	 // 数值绑定枚举
    public void setEnable(int enable) {
        this.enable = enable;
        this.deleteEnum = DeleteEnum.findByValue(enable);
    }

	 // 枚举映射数值
    public DeleteEnum getDeleteEnum() {
        this.enable = this.deleteEnum.value;
        return deleteEnum;
    }
	
	 // 枚举映射数值
    public void setDeleteEnum(DeleteEnum deleteEnum) {
        this.deleteEnum = deleteEnum;
        this.enable = this.deleteEnum.value;
    }
}

复制代码

我们保持示例中Controller类和测试用例的代码不变,执行用例.

  • 测试对象序列化到前端展示执行结果
{
  "code": "0",
  "data": "{"crateTime":"2016-12-2808: 12: 52","deleteEnum":"ENABLE","enable":2,"updateTime":"2016-12-2808: 12: 52","id":1,"name":"测试Fastjson","age":11}"
}
复制代码
  • 测试提交数据转换成模型 执行后台打印
{"crateTime":"2016-12-28 08:20:17","deleteEnum":"ENABLE","enable":2,"updateTime":"2016-12-28 08:20:17","id":1,"name":"测试Fastjson","age":11}

复制代码

修改用例模拟的实体对象, 测试数值到枚举的映射.

ModelTest modelTest = new ModelTest();
modelTest.setAge(11);
modelTest.setCrateTime(new Date());
modelTest.setModifyTime(new Date());
modelTest.setId(1L);
modelTest.setName("测试Fastjson");
modelTest.setRemark("备注");
modelTest.setEnable(1);
复制代码

后台打印输出:

{"crateTime":"2016-12-28 08:21:48","deleteEnum":"DISABLE","enable":1,"updateTime":"2016-12-28 08:21:48","id":1,"name":"测试Fastjson","age":11}
复制代码

可以看到enable的值自动和枚举类型映射上.

使用FastJson做对象和JSON之间的转换

对象转换成JSON

@Test
    public void fastJSONTest03() {
        ModelTest modelTest = new ModelTest();
        modelTest.setAge(11);
        modelTest.setCrateTime(new Date());
        modelTest.setModifyTime(new Date());
        modelTest.setId(1L);
        modelTest.setName("测试Fastjson");
        modelTest.setRemark("备注");
        modelTest.setEnable(1);
        System.out.println(JSON.toJSONString(modelTest));
    }
复制代码

执行结果:

{"crateTime":"2016-12-28 08:30:42","deleteEnum":"DISABLE","enable":1,"updateTime":"2016-12-28 08:30:42","id":1,"name":"测试Fastjson","age":11}
复制代码

JSON转换成对象

@Test
public void fastJSONTest04() {
   String json = "{\n" +
           "  \"id\": 1,\n" +
           "  \"deleteEnum\": \"DISABLE\",\n" +
           "  \"updateTime\": 1482884802549,\n" +
           "  \"crateTime\": 1482884802549,\n" +
           "  \"age\": 11,\n" +
           "  \"name\": \"测试Fastjson\",\n" +
           "  \"enable\": 1\n" +
           "}";
   ModelTest modelTest = JSONObject.parseObject(json, ModelTest.class);
   System.out.println(modelTest);
   System.out.println(modelTest.getName());
}
复制代码

执行结果:

ModelTest{id=1, name='测试Fastjson', age=11, remark='null', crateTime=Wed Dec 28 08:26:42 CST 2016, modifyTime=Wed Dec 28 08:26:42 CST 2016, deleteEnum=DISABLE, enable=1}
测试Fastjson
复制代码

示例中直接打印输出对象,是因为复写了toString()方法.

猜你喜欢

转载自juejin.im/post/5dfb298ae51d455811340650