From 0ca32ec9229b60caeb0ff6f2553e9dec251dbc7b Mon Sep 17 00:00:00 2001 From: Calvin Date: Sat, 31 Mar 2012 23:28:14 +0800 Subject: [PATCH] =?UTF-8?q?#28=20=20Jackson=E6=BC=94=E7=A4=BA=E6=9B=B4?= =?UTF-8?q?=E6=96=B0,=E5=8A=A0=E5=85=A5=E6=9B=B4=E5=A4=9A=E6=BC=94?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../showcase/utilities/json/JsonDemo.java | 332 +++++++++++------- .../springside/modules/mapper/JsonMapper.java | 16 +- 2 files changed, 208 insertions(+), 140 deletions(-) diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/utilities/json/JsonDemo.java b/examples/showcase/src/main/java/org/springside/examples/showcase/utilities/json/JsonDemo.java index 0c7d6785..98e4a004 100644 --- a/examples/showcase/src/main/java/org/springside/examples/showcase/utilities/json/JsonDemo.java +++ b/examples/showcase/src/main/java/org/springside/examples/showcase/utilities/json/JsonDemo.java @@ -9,19 +9,23 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; 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.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonManagedReference; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; 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; @@ -45,6 +49,8 @@ public class JsonDemo { private static JsonMapper mapper = JsonMapper.buildNonDefaultMapper(); + //// 基本操作 演示 //// + /** * 序列化对象/集合到Json字符串. */ @@ -139,6 +145,185 @@ public class JsonDemo { assertEquals("{\"name\":\"A\"}", nonDefaultMaper.toJson(bean)); } + /* + * 测试类似Jaxb的常用annotaion,如properName,ignore,propertyOrder + */ + @Test + public void jaxbStyleAnnoation() { + TestBean2 testBean = new TestBean2(1, "foo", 18); + //结果name属性输出在前,且被改名为productName,且age属性被ignore + assertEquals("{\"productName\":\"foo\",\"id\":1}", mapper.toJson(testBean)); + } + + //调转顺序 + @JsonPropertyOrder({ "name", "id" }) + public static class TestBean2 { + + public long id; + + @JsonProperty("productName") + public String name; + + @JsonIgnore + public int age; + + public TestBean2() { + + } + + public TestBean2(long id, String name, int age) { + this.id = id; + this.name = name; + this.age = age; + } + + } + + /** + * 測試輸出jsonp格式內容. + */ + @Test + public void jsonp() { + TestBean bean = new TestBean("foo"); + String jsonpString = mapper.toJsonP("callback", bean); + assertEquals("callback({\"name\":\"foo\"})", jsonpString); + } + + /** + * 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, 主要演示不同風格的Mapper對Null值,初始化後沒改變過的屬性值的處理. + */ + public static class TestBean { + + private String name; + private String defaultValue = "hello"; //默认值没被修改过的属性,可能会不序列化 + private String nullValue = null; //空值的据行,可能会不序列化 + + public TestBean() { + } + + public TestBean(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public String getNullValue() { + return nullValue; + } + + public void setNullValue(String nullValue) { + this.nullValue = nullValue; + } + + @Override + public String toString() { + return "TestBean [defaultValue=" + defaultValue + ", name=" + name + ", nullValue=" + nullValue + "]"; + } + } + + ////特殊数据类型演示//// + + /** + * 测试对枚举的序列化,可以選擇用一個int字段(但不是order)而不是以Name來序列化,以減少長度. + */ + @Test + public void enumData() { + //默認使用enum.name() + assertEquals("\"One\"", mapper.toJson(TestEnum.One)); + assertEquals(TestEnum.One, mapper.fromJson("\"One\"", TestEnum.class)); + + //使用enum.toString() + //注意,index會通過toString序列成字符串而不是int,否則又和順序號混淆. + //注意配置必須在所有讀寫動作之前調用. + JsonMapper newMapper = JsonMapper.buildNormalMapper(); + newMapper.enableEnumUseToString(); + assertEquals("\"1\"", newMapper.toJson(TestEnum.One)); + assertEquals(TestEnum.One, newMapper.fromJson("\"1\"", TestEnum.class)); + } + + /** + * 枚舉類型的演示Bean. + */ + public static enum TestEnum { + One(1), Two(2), Three(3); + + private int index; + + TestEnum(int index) { + this.index = index; + } + + @Override + public String toString() { + return String.valueOf(index); + } + } + + /** + * 测试对日期的序列化,日期默认以Timestamp方式存储,也可以用@JsonFormat格式化. + */ + @Test + public void dateData() { + DateBean dateBean = new DateBean(); + Date date = new Date(); + String timestampString = String.valueOf(date.getTime()); + String format = "yyyy-MM-dd HH:mm:ss"; + String formatedString = new DateTime(date).toString(format); + + dateBean.startDate = date; + dateBean.endDate = date; + + //to json + String expectedJson = "{\"startDate\":" + timestampString + ",\"endDate\":\"" + formatedString + "\"}"; + assertEquals(expectedJson, mapper.toJson(dateBean)); + + //from json + Date expectedEndDate = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").parseDateTime(formatedString).toDate(); + + DateBean resultBean = mapper.fromJson(expectedJson, DateBean.class); + assertEquals(date, resultBean.startDate); + assertEquals(expectedEndDate, resultBean.endDate); + + } + + public static class DateBean { + + public Date startDate; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08:00") + public Date endDate; + + } + /** * 测试传入空对象,空字符串,Empty的集合,"null"字符串的结果. */ @@ -176,55 +361,7 @@ public class JsonDemo { assertEquals(0, nullListResult.size()); } - /** - * 测试对枚举的序列化,可以選擇用一個int字段(但不是order)而不是以Name來序列化,以減少長度. - */ - @Test - public void enumData() { - //默認使用enum.name() - assertEquals("\"One\"", mapper.toJson(TestEnum.One)); - assertEquals(TestEnum.One, mapper.fromJson("\"One\"", TestEnum.class)); - - //使用enum.toString() - //注意,index會通過toString序列成字符串而不是int,否則又和順序號混淆. - //注意配置必須在所有讀寫動作之前調用. - JsonMapper newMapper = JsonMapper.buildNormalMapper(); - newMapper.setEnumUseToString(true); - assertEquals("\"1\"", newMapper.toJson(TestEnum.One)); - assertEquals(TestEnum.One, newMapper.fromJson("\"1\"", TestEnum.class)); - } - - /** - * 枚舉類型的演示Bean. - */ - public static enum TestEnum { - One(1), Two(2), Three(3); - - private int index; - - TestEnum(int index) { - this.index = index; - } - - @Override - public String toString() { - return String.valueOf(index); - } - } - - /** - * 测试对日期的序列化,日期默认以Timestamp方式存储. - */ - @Test - public void dateData() { - Date date = new Date(); - String tsString = String.valueOf(date.getTime()); - - assertEquals(tsString, mapper.toJson(date)); - - assertEquals(date, mapper.fromJson(tsString, Date.class)); - } - + //// 高级应用 //// /** * 測試父子POJO間的循環引用. */ @@ -304,22 +441,6 @@ public class JsonDemo { } } - /** - * 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版本升级时固定属性的变动. */ @@ -442,6 +563,8 @@ public class JsonDemo { } + ////批量定制行为//// + /** * 测试 自定义转换器,整体感觉稍显复杂。目标是将Money和Long互转. */ @@ -449,20 +572,24 @@ public class JsonDemo { public void customConverter() { JsonMapper newMapper = JsonMapper.buildNonNullMapper(); - SimpleModule testModule = new SimpleModule("MyModule", Version.unknownVersion()); - testModule.addSerializer(new MoneySerializer()); - testModule.addDeserializer(Money.class, new MoneyDeserializer()); - newMapper.getMapper().registerModule(testModule); - Money money = new Money(1.2); + SimpleModule moneyModule = new SimpleModule("MoneyModule"); + moneyModule.addSerializer(new MoneySerializer()); + moneyModule.addDeserializer(Money.class, new MoneyDeserializer()); + newMapper.getMapper().registerModule(moneyModule); - String jsonString = newMapper.toJson(money); + //tojson + User user = new User(); + user.setName("foo"); + user.setSalary(new Money(1.2)); - assertEquals("\"1.2\"", jsonString); + String jsonString = newMapper.toJson(user); - Money resultMoney = newMapper.fromJson(jsonString, Money.class); + assertEquals("{\"name\":\"foo\",\"salary\":\"1.2\"}", jsonString); - assertEquals(new Double(1.2), resultMoney.value); + //from + User resultUser = newMapper.fromJson(jsonString, User.class); + assertEquals(new Double(1.2), resultUser.getSalary().value); } @@ -554,59 +681,4 @@ public class JsonDemo { } } - /** - * 測試輸出jsonp格式內容. - */ - @Test - public void jsonp() { - TestBean bean = new TestBean("foo"); - String jsonpString = mapper.toJsonP("callback", bean); - assertEquals("callback({\"name\":\"foo\"})", jsonpString); - } - - /** - * 演示Bean, 主要演示不同風格的Mapper對Null值,初始化後沒改變過的屬性值的處理. - */ - public static class TestBean { - - private String name; - private String defaultValue = "hello"; //默认值没被修改过的属性,可能会不序列化 - private String nullValue = null; //空值的据行,可能会不序列化 - - public TestBean() { - } - - public TestBean(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDefaultValue() { - return defaultValue; - } - - public void setDefaultValue(String defaultValue) { - this.defaultValue = defaultValue; - } - - public String getNullValue() { - return nullValue; - } - - public void setNullValue(String nullValue) { - this.nullValue = nullValue; - } - - @Override - public String toString() { - return "TestBean [defaultValue=" + defaultValue + ", name=" + name + ", nullValue=" + nullValue + "]"; - } - } } diff --git a/modules/core/src/main/java/org/springside/modules/mapper/JsonMapper.java b/modules/core/src/main/java/org/springside/modules/mapper/JsonMapper.java index 42a4eb01..f45f4b52 100644 --- a/modules/core/src/main/java/org/springside/modules/mapper/JsonMapper.java +++ b/modules/core/src/main/java/org/springside/modules/mapper/JsonMapper.java @@ -32,25 +32,21 @@ public class JsonMapper { private ObjectMapper mapper; - public JsonMapper() { - this(Include.ALWAYS); - } - public JsonMapper(Include include) { mapper = new ObjectMapper(); //设置输出时包含属性的风格 mapper.setSerializationInclusion(include); //设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性 - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); //禁止使用int代表Enum的order()來反序列化Enum,非常危險 - mapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true); + mapper.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS); } /** * 创建输出全部属性到Json字符串的Mapper. */ public static JsonMapper buildNormalMapper() { - return new JsonMapper(); + return new JsonMapper(Include.ALWAYS); } /** @@ -162,9 +158,9 @@ public class JsonMapper { * 為False時時使用Enum的name()函數來讀寫Enum, 默認為False. * 注意本函數一定要在Mapper創建後, 所有的讀寫動作之前調用. */ - public void setEnumUseToString(boolean value) { - mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, value); - mapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, value); + public void enableEnumUseToString() { + mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); + mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); } /**