现今的项目,有很多会用到json的输入输出,如手机终端、网页ajax异步请求等。下面是我自己遇到在java项目中较好的使用json输入输出的方式
一、给spring配置输入输出json的支持
1. 引入jackson的包
jackson-core-asl-1.9.13.jar jackson-mapper-asl-1.9.13.jar jackson-core-lgpl-1.9.13.jar jackson-mapper-lgpl-1.9.13.jar
在此提供maven配置:
<dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-core-asl</artifactId> <version>1.9.13</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.13</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-core-lgpl</artifactId> <version>1.9.13</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-lgpl</artifactId> <version>1.9.13</version> <type>jar</type> <scope>compile</scope> </dependency>
2.在spring配置文件中加入
<bean class ="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" > <property name="messageConverters"> <list> <ref bean="mappingJacksonHttpMessageConverter" /><!-- json转换器 --> </list> </property> </bean> <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" > </bean>
3.在使用要输入输出的controller方法加上@RequestBody 和 @ResponseBody
//JsonRequest和JsonResponse是自定义的请求与返回实体 public @ResponseBody JsonResponse process(@RequestBody JsonRequest request) { return new JsonResponse(); }
JsonResponse.java
public class Response implements Serializable { private static final long serialVersionUID = 7015731184314121850L; private String cmd; private String token; public String getCmd() { return cmd; } public void setCmd(String cmd) { this.cmd = cmd; } public String getToken() { return token; } public void setToken(String token) { this.token = token; } }
JsonRequest.java
public class JsonRequest { String cmd; String cmdtype; public String getCmd() { return cmd; } public void setCmd(String cmd) { this.cmd = cmd; } public String getCmdtype() { return cmdtype; } public void setCmdtype(String cmdtype) { this.cmdtype = cmdtype; } }
至此,简单javaBean自动序列化为json字符串的配置就完成了。
测试举例
header
{"Content-Type":"application/json"}
json请求
{ "cmd":"get msg", "cmdtype":"" }
json返回
{ "cmd": "get msg", "token": "" }
二、利用jackson的多态,spring自动识别子类
我们还会遇到很多包含有类成员变量的复杂实体,如JsonRequest里有Data自定义类变量,那该如何转换?
JsonRequest.java
public class Request { String cmd; String cmdtype; Data data; // data支持的类型:基本数据类型及对应包装类,Collection、Map的实现类,JavaBean public String getCmd() { return cmd; } public void setCmd(String cmd) { this.cmd = cmd; } public String getCmdtype() { return cmdtype; } public void setCmdtype(String cmdtype) { this.cmdtype = cmdtype; } public Data getData() { return data; } public void setData(Data data) { this.data = data; } }
1. jackson的多态
Jackson数据绑定可以很方便的将java的对象类型和json数据格式之间进行转换。对于有多个子类型的多态集成结构的对象,Jackson在序列化的时候加入一些类型信息,可以在反序列化的时候准确的还原某个类型的子类。
方式一
objectMapper.enableDefaultTyping(); // default to using DefaultTyping.OBJECT_AND_NON_CONCRETE objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
DefaultTyping 有四个选项 JAVA_LANG_OBJECT: 当对象属性类型为Object时生效 OBJECT_AND_NON_CONCRETE: 当对象属性类型为Object或者非具体类型(抽象类和接口)时生效 NON_CONCRETE_AND+_ARRAYS: 同上, 另外所有的数组元素的类型都是非具体类型或者对象类型 NON_FINAL: 对所有非final类型或者非final类型元素的数组 开启DefaultTyping,并且让所有的非final类型对象持久化时都存储类型信息显然能准确的反序列多态类型的数据。
方式二
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class") class Animal { }
use=JsonTypeInfo.Id.CLASS: 使用类的完全限定名作为唯一识别 include=JsonTypeInfo.As.PROPERTY: 将这个唯一识别的字段保存为属性值 property="@class" 该属性值的名称为 @class use的几个可选值 CLASS 完全限定名 MINIMAL_CLASS 类名,若基类和子类在同一包类,会省略包名 NAME 逻辑名,需要单独定义名称与类的对应关系 CUSTOM 由@JsonTypeIdResolver对应 include的几个选值 PROPERTY 将属性包含在对象成员属性里 WRAPPER_OBJECT 属性作为键,序列化的对象作为值 WRAPPER_ARRAY 第一个元素是类型ID,第二原始是序列化的对象
参考链接:http://codelife.me/blog/2012/11/03/jackson-polymorphic-deserialization/
2.为Class添加@JsonTypeInfo
我所见过的是使用方式二@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
Data接口 Data.java
//以下注解含义:如何从Json中辨认Data的实现类?通过Json对象中的"dt"字段来辨别,通过其值找到对应的实现类。 @JsonTypeInfo(include = As.PROPERTY, property = "dt", use = Id.NAME) public interface Data extends Serializable { }
名为data_impl的Data接口其中一个实现类 DataImpl.java
@JsonTypeName("data_impl") public class DataImpl implements Data{ private static final long serialVersionUID = 6904642231937403932L; private String name; private Integer value; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getValue() { return value; } public void setValue(Integer value) { this.value = value; } }
3. Spring 中 BeanPostProcessor接口的介绍
public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }
BeanPostProcessor是spring对bean实体预处理的接口,实现了BeanPostProcessor接口的类被注入给spring管理后,会自动在加载实体前后调用其方法。
由于Spring3.0与Spring3.1版本有不同,所以结合完jackson的处理略有不同,否则,有子类的json串将报具体代码如下:
@Component public class JsonObjectMapperConfiguration implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @SuppressWarnings("deprecation") public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { //spring 3.1以上 if (bean instanceof RequestMappingHandlerAdapter) { RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean; List<HttpMessageConverter<?>> converters = adapter.getMessageConverters(); for (HttpMessageConverter<?> converter : converters) { if (converter instanceof MappingJacksonHttpMessageConverter) { MappingJacksonHttpMessageConverter jsonConverter = (MappingJacksonHttpMessageConverter) converter; ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, true); SerializationConfig config = mapper.getSerializationConfig(); config.setSerializationInclusion(Inclusion.NON_NULL); mapper.registerSubtypes(DataImpl.class); jsonConverter.setObjectMapper(mapper); } } } /* sping3.0 if (bean instanceof AnnotationMethodHandlerAdapter) { AnnotationMethodHandlerAdapter adapter = (AnnotationMethodHandlerAdapter) bean; HttpMessageConverter<?>[] converters = adapter.getMessageConverters(); for (HttpMessageConverter<?> converter : converters) { if (converter instanceof MappingJacksonHttpMessageConverter) { MappingJacksonHttpMessageConverter jsonConverter = (MappingJacksonHttpMessageConverter) converter; ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, true); SerializationConfig config = mapper.getSerializationConfig(); config.setSerializationInclusion(Inclusion.NON_NULL); mapper.registerSubtypes(DataImpl.class); jsonConverter.setObjectMapper(mapper); } } } */ return bean; } }
现在就可以在请求的json里加入带Data的类成员变量参数了
请求加入data后
{ "cmd": "get msg", "cmdtype": "", "data": { "dt": "data_impl", "name": "ok", "value": 2 } }
返回也可加入data
{ "cmd": "12323", "token": "ok=2", "data": { "dt": "data_result", "name": "okasdf" } }
三、总结
以上就是要达到想要的效果,可以使用dt定义接口数据类型,cmd定义api名称。