提交 993ed015 编写于 作者: C Calvin

#28 Jackson从1.9升级到2.x,连带Jersey的Json使用进行修改

上级 541ccc57
com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider
\ No newline at end of file
com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider
\ No newline at end of file
......@@ -40,11 +40,6 @@
<servlet>
<servlet-name>JerseyServlet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<!-- 使用Jackson for JSON格式 -->
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>JerseyServlet</servlet-name>
......
......@@ -9,32 +9,30 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.annotate.JsonAnyGetter;
import org.codehaus.jackson.annotate.JsonAnySetter;
import org.codehaus.jackson.annotate.JsonBackReference;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonManagedReference;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.MapperConfig;
import org.codehaus.jackson.map.ObjectWriter;
import org.codehaus.jackson.map.PropertyNamingStrategy;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.annotate.JsonView;
import org.codehaus.jackson.map.deser.std.StdDeserializer;
import org.codehaus.jackson.map.introspect.AnnotatedMethod;
import org.codehaus.jackson.map.module.SimpleModule;
import org.codehaus.jackson.map.ser.std.SerializerBase;
import org.codehaus.jackson.type.JavaType;
import org.joda.time.DateTime;
import org.junit.Test;
import org.springside.modules.mapper.JsonMapper;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
......@@ -113,7 +111,7 @@ public class JsonDemo {
//List<Bean>
String beanListString = "[{\"name\":\"A\"},{\"name\":\"B\"}]";
JavaType beanListType = mapper.constructParametricType(List.class, TestBean.class);
JavaType beanListType = mapper.createCollectionType(List.class, TestBean.class);
List<TestBean> beanList = mapper.fromJson(beanListString, beanListType);
System.out.println("Bean List:");
for (TestBean element : beanList) {
......@@ -179,7 +177,7 @@ public class JsonDemo {
}
/**
* 测试对枚举的序列化,可以選擇用一個int字段而不是以Name來序列化,以減少長度.
* 测试对枚举的序列化,可以選擇用一個int字段(但不是order)而不是以Name來序列化,以減少長度.
*/
@Test
public void enumData() {
......@@ -215,61 +213,49 @@ public class JsonDemo {
}
/**
* 测试对日期的序列化.
* 测试对日期的序列化,日期默认以Timestamp方式存储.
*/
@Test
public void dateData() {
DateTime jodaDate = new DateTime();
//日期默认以Timestamp方式存储
Date date = new Date(jodaDate.getMillis());
String tsString = String.valueOf(jodaDate.getMillis());
Date date = new Date();
String tsString = String.valueOf(date.getTime());
assertEquals(tsString, mapper.toJson(date));
assertEquals(date, mapper.fromJson(tsString, Date.class));
}
/**
* JSON字符串裡只含有Bean中部分的屬性時,更新一個已存在Bean,只覆蓋部分的屬性.
*/
@Test
public void updateBean() {
String jsonString = "{\"name\":\"A\"}";
TestBean bean = new TestBean();
bean.setDefaultValue("Foobar");
bean = mapper.update(bean, jsonString);
assertEquals("A", bean.getName());
assertEquals("Foobar", bean.getDefaultValue());
}
/**
* 測試父子POJO間的循環引用.
*/
@Test
public void parentChildBean() {
//初始化对象关系,parent的Childs里含有 child1,child2, child1/child2的parent均指向parent.
//初始化对象关系,parent的children里含有 child1,child2, child1/child2的parent均指向parent.
ParentChildBean parent = new ParentChildBean("parent");
ParentChildBean child1 = new ParentChildBean("child1");
child1.setParent(parent);
parent.getChilds().add(child1);
parent.getChildren().add(child1);
ParentChildBean child2 = new ParentChildBean("child2");
child2.setParent(parent);
parent.getChilds().add(child2);
parent.getChildren().add(child2);
String jsonString = "{\"name\":\"parent\",\"childs\":[{\"name\":\"child1\"},{\"name\":\"child2\"}]}";
//打印parent的json输出,json字符串裡childs中的child1/child2都不包含到parent的屬性
//序列化是, json字符串裡children中的child1/child2都不包含到parent的屬性
String jsonString = "{\"name\":\"parent\",\"children\":[{\"name\":\"child1\"},{\"name\":\"child2\"}]}";
assertEquals(jsonString, mapper.toJson(parent));
//注意此時如果單獨打印child1,也不會打印parent,信息將丟失。
//注意此時如果單獨序列化child1,也不會打印parent,信息將丟失。
assertEquals("{\"name\":\"child1\"}", mapper.toJson(child1));
//反向序列化时,Json已很聪明的把parent填入child1/child2中.
ParentChildBean parentResult = mapper.fromJson(jsonString, ParentChildBean.class);
assertEquals("parent", parentResult.getChilds().get(0).getParent().getName());
assertEquals("parent", parentResult.getChildren().get(0).getParent().getName());
//单独反序列化child1,当然parent也是空
ParentChildBean child1Result = mapper.fromJson("{\"name\":\"child1\"}", ParentChildBean.class);
assertNull(child1Result.parent);
assertEquals("child1", child1Result.getName());
}
/**
......@@ -279,7 +265,7 @@ public class JsonDemo {
private String name;
private ParentChildBean parent;
private List<ParentChildBean> childs = Lists.newArrayList();
private List<ParentChildBean> children = Lists.newArrayList();
public ParentChildBean() {
}
......@@ -308,16 +294,32 @@ public class JsonDemo {
}
@JsonManagedReference
public List<ParentChildBean> getChilds() {
return childs;
public List<ParentChildBean> getChildren() {
return children;
}
@JsonManagedReference
public void setChilds(List<ParentChildBean> childs) {
this.childs = childs;
public void setChildren(List<ParentChildBean> children) {
this.children = children;
}
}
/**
* JSON字符串裡只含有Bean中部分的屬性時,更新一個已存在Bean,只覆蓋部分的屬性.
*/
@Test
public void updateBean() {
String jsonString = "{\"name\":\"A\"}";
TestBean bean = new TestBean();
bean.setDefaultValue("Foobar");
bean = mapper.update(bean, jsonString);
//name被赋值
assertEquals("A", bean.getName());
//DefaultValue不在Json串中,依然保留。
assertEquals("Foobar", bean.getDefaultValue());
}
/**
* 測試可擴展Bean,會自動的把確定的屬性放入固定的成員變量, 其他屬性放到一个类型为Map的成员变量裡,能很好的支持Bean版本升级时固定属性的变动.
*/
......@@ -326,8 +328,11 @@ public class JsonDemo {
//一个没有区分是变量还是Map的普通JSON字符串.
String jsonString = "{\"name\" : \"Foobar\",\"age\" : 37,\"occupation\" : \"coder man\"}";
ExtensibleBean extensibleBean = mapper.fromJson(jsonString, ExtensibleBean.class);
//固定属性
assertEquals("Foobar", extensibleBean.getName());
assertEquals(null, extensibleBean.getProperties().get("name"));
//可扩展属性
assertEquals("coder man", extensibleBean.getProperties().get("occupation"));
}
......@@ -335,8 +340,9 @@ public class JsonDemo {
* 演示用的可擴展Bean.@JsonAnySetter与@JsonAnyGetter是关键.
*/
public static class ExtensibleBean {
private String name; // we always have name
// 固定属性
private String name;
// 扩展属性
private Map<String, String> properties = Maps.newHashMap();
public ExtensibleBean() {
......@@ -372,16 +378,14 @@ public class JsonDemo {
viewBean.setOtherValue("others");
viewBean.setIgnoreValue("ignored");
//public view
ObjectWriter publicWriter = mapper.getMapper().writerWithView(Views.Public.class);
assertEquals("{\"name\":\"Foo\",\"otherValue\":\"others\"}", publicWriter.writeValueAsString(viewBean));
//internal view
ObjectWriter internalWriter = mapper.getMapper().writerWithView(Views.Internal.class);
assertEquals("{\"age\":16,\"otherValue\":\"others\"}", internalWriter.writeValueAsString(viewBean));
//設置默認是否顯示沒有用@Json定義的屬性
JsonMapper newMapper = JsonMapper.buildNormalMapper();
newMapper.getMapper().configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
publicWriter = newMapper.getMapper().writerWithView(Views.Public.class);
assertEquals("{\"name\":\"Foo\"}", publicWriter.writeValueAsString(viewBean));
}
public static class Views {
......@@ -439,14 +443,14 @@ public class JsonDemo {
}
/**
* 测试自定义转换器
* 测试 自定义转换器,整体感觉稍显复杂。目标是将Money和Long互转.
*/
@Test
public void customConverter() {
JsonMapper newMapper = JsonMapper.buildNonNullMapper();
SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null));
testModule.addSerializer(new MoneySerializer()); // assuming serializer declares correct class to bind to
SimpleModule testModule = new SimpleModule("MyModule", Version.unknownVersion());
testModule.addSerializer(new MoneySerializer());
testModule.addDeserializer(Money.class, new MoneyDeserializer());
newMapper.getMapper().registerModule(testModule);
......@@ -462,7 +466,7 @@ public class JsonDemo {
}
public class MoneySerializer extends SerializerBase<Money> {
public class MoneySerializer extends StdSerializer<Money> {
public MoneySerializer() {
super(Money.class);
}
......@@ -505,11 +509,35 @@ public class JsonDemo {
}
/**
* 测试修改属性名策略
* @throws JsonMappingException
* 包含Money属性的对象.
*/
public static class User {
private String name;
private Money salary;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Money getSalary() {
return salary;
}
public void setSalary(Money salary) {
this.salary = salary;
}
}
/**
* 测试修改 属性名策略
*/
@Test
public void customPropertyNameing() throws JsonMappingException {
public void customPropertyNaming() throws JsonMappingException {
TestBean bean = new TestBean("foo");
bean.setDefaultValue("bar");
......
......@@ -7,7 +7,6 @@ import javax.ws.rs.core.MediaType;
import org.springframework.beans.factory.annotation.Required;
import org.springside.examples.showcase.common.entity.User;
import org.springside.examples.showcase.webservice.rs.dto.UserDTO;
import org.springside.modules.mapper.JsonMapper;
import org.springside.modules.rest.jersey.Jerseys;
import org.springside.modules.web.Servlets;
......@@ -56,8 +55,7 @@ public class UserResourceClient {
* 无公共DTO类定义, 取得返回JSON字符串后自行转换DTO.
*/
public UserDTO searchUserReturnJson(String name) {
String jsonString = client.path("/users/search").queryParam("name", name).get(String.class);
return JsonMapper.buildNormalMapper().fromJson(jsonString, UserDTO.class);
return client.path("/users/search").queryParam("name", name).get(UserDTO.class);
}
/**
......
com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider
\ No newline at end of file
com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider
\ No newline at end of file
......@@ -40,11 +40,6 @@
<artifactId>spring-webmvc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
......@@ -55,7 +50,7 @@
<!-- REST begin -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<artifactId>jersey-server</artifactId>
<optional>true</optional>
</dependency>
<dependency>
......@@ -104,6 +99,11 @@
<artifactId>joda-time</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
......
......@@ -8,16 +8,17 @@ package org.springside.modules.mapper;
import java.io.IOException;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import org.codehaus.jackson.map.util.JSONPObject;
import org.codehaus.jackson.type.JavaType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.util.JSONPObject;
/**
* 简单封装Jackson,实现JSON String<->Java Object的Mapper.
*
......@@ -31,42 +32,46 @@ public class JsonMapper {
private ObjectMapper mapper;
public JsonMapper(Inclusion inclusion) {
public JsonMapper() {
this(Include.ALWAYS);
}
public JsonMapper(Include include) {
mapper = new ObjectMapper();
//设置输出时包含属性的风格
mapper.setSerializationInclusion(inclusion);
mapper.setSerializationInclusion(include);
//设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性
mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//禁止使用int代表Enum的order()來反序列化Enum,非常危險
mapper.configure(DeserializationConfig.Feature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
mapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
}
/**
* 创建输出全部属性到Json字符串的Mapper.
*/
public static JsonMapper buildNormalMapper() {
return new JsonMapper(Inclusion.ALWAYS);
return new JsonMapper();
}
/**
* 创建只输出非空属性到Json字符串的Mapper.
*/
public static JsonMapper buildNonNullMapper() {
return new JsonMapper(Inclusion.NON_NULL);
return new JsonMapper(Include.NON_NULL);
}
/**
* 创建只输出初始值被改变的属性到Json字符串的Mapper.
*/
public static JsonMapper buildNonDefaultMapper() {
return new JsonMapper(Inclusion.NON_DEFAULT);
return new JsonMapper(Include.NON_DEFAULT);
}
/**
* 创建只输出非Null且非Empty(如List.isEmpty)的属性到Json字符串的Mapper.
*/
public static JsonMapper buildNonEmptyMapper() {
return new JsonMapper(Inclusion.NON_EMPTY);
return new JsonMapper(Include.NON_EMPTY);
}
/**
......@@ -88,7 +93,7 @@ public class JsonMapper {
* 如果JSON字符串为"[]", 返回空集合.
*
* 如需读取集合如List/Map, 且不是List<String>这种简单类型时,先使用函數constructParametricType构造类型.
* @see #constructParametricType(Class, Class...)
* @see #createCollectionType(Class, Class...)
*/
public <T> T fromJson(String jsonString, Class<T> clazz) {
if (StringUtils.isEmpty(jsonString)) {
......@@ -108,7 +113,7 @@ public class JsonMapper {
* 如果JSON字符串为"[]", 返回空集合.
*
* 如需读取集合如List/Map, 且不是List<String>这种简单类型时,先使用函數constructParametricType构造类型.
* @see #constructParametricType(Class, Class...)
* @see #createCollectionType(Class, Class...)
*/
public <T> T fromJson(String jsonString, JavaType javaType) {
if (StringUtils.isEmpty(jsonString)) {
......@@ -124,11 +129,11 @@ public class JsonMapper {
}
/**
* 構造泛型的Type如List<MyBean>, 则调用constructParametricType(ArrayList.class,MyBean.class)
* 構造泛型的Type如List<MyBean>, 则调用constructCollectionType(ArrayList.class,MyBean.class)
* Map<String,MyBean>则调用(HashMap.class,String.class, MyBean.class)
*/
public JavaType constructParametricType(Class<?> parametrized, Class<?>... parameterClasses) {
return mapper.getTypeFactory().constructParametricType(parametrized, parameterClasses);
public JavaType createCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
}
/**
......@@ -158,8 +163,8 @@ public class JsonMapper {
* 注意本函數一定要在Mapper創建後, 所有的讀寫動作之前調用.
*/
public void setEnumUseToString(boolean value) {
mapper.configure(SerializationConfig.Feature.WRITE_ENUMS_USING_TO_STRING, value);
mapper.configure(DeserializationConfig.Feature.READ_ENUMS_USING_TO_STRING, value);
mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, value);
mapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, value);
}
/**
......
......@@ -15,9 +15,6 @@ import org.slf4j.LoggerFactory;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.json.JSONConfiguration;
public class Jerseys {
......@@ -27,12 +24,10 @@ public class Jerseys {
}
/**
* 创建JerseyClient, 设定JSON字符串使用Jackson解析.
* 创建JerseyClient.
*/
public static WebResource createClient(String baseUrl) {
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
Client client = Client.create(clientConfig);
Client client = Client.create();
return client.resource(baseUrl);
}
......
......@@ -7,15 +7,15 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.codehaus.jackson.type.TypeReference;
import org.junit.Test;
import org.springside.modules.mapper.JsonMapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* 测试Jackson对Object,Map,List,数组,枚举,日期类等的持久化.
* 更多测试见showcase中的JsonDemo.
*
* @author calvin
*/
......
......@@ -24,7 +24,7 @@
<activemq.version>5.5.1</activemq.version>
<jetty.version>7.6.2.v20120308</jetty.version>
<h2.version>1.3.164</h2.version>
<jackson.version>1.9.5</jackson.version>
<jackson.version>2.0.0</jackson.version>
<slf4j.version>1.6.4</slf4j.version>
<selenium.version>2.20.0</selenium.version>
......@@ -323,17 +323,6 @@
<artifactId>jersey-client</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>${jersey.version}</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-spring</artifactId>
......@@ -545,25 +534,21 @@
<!-- JSON begin -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>${jackson.version}</version>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- JSON end -->
<!-- GENERAL UTILS begin -->
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册