原来 Java 项目中用的 JSON 组件库主要是 Gson 和 json-lib,Gson 算是很错的库,json-lib 略显寒碜。好啦,最近 Play 2.x 中弃用了 Gson 而采纳了 Jackson,所以现在就来打探一下 Jackson,踩个点吧。
Jackson 号称非常高的性能,听说比另两位兄弟 Gson 和 json-lib 高出一大截,我没有亲测,可是有心人做了,看这个链接 两款JSON类库Jackson与JSON-lib的性能对比(新增第三款测试) 中的数据。2010 年 8 月份的测试结果,不知现在随着版本的变更是否仍然保持着这种悬殊。
通常我会在把文章开头塞丰满,做足前戏,并不是因为在天涯混习惯了的缘故,况且我在天涯总是讷于言的; 在这里,自己的地盘自己作主,不会有要求码足多少字才能发表的自虐性需求,仅仅是让本文在主页上显示时的的概要不空洞而已,可以简单粗暴的称之为废话。
了了,先了解 Jacson 最贴近实际应用场景的应用,即 Jackson 怎么把一个 Java 生成对应的 JSON 字符串,看看前面的文字有这么多了,直接上一段代码吧,而后再慢慢假设与分解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
package cc.unmi.testjackson; import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; import org.codehaus.jackson.annotate.JsonMethod; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; /** * @author Unmi */ public class TestClient { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(JsonMethod.FIELD, Visibility.ANY); mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true); Person person = new Person("Unmi", new Address("JiangXi")); String json = mapper.writeValueAsString(person); System.out.println(json); } } class Person { private String name; private Address address; public Person(String name, Address address){ this.name = name; this.address = address; } } class Address { private String province; public Address(String province){ this.province = province; } public String getProvince(){ return this.province; } } |
要使用 Jackson,自然要引入相应的 jar 包,用 Maven 管理依赖的在 pom.xml 中加上:
1 2 3 4 5 |
<dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.11</version> </dependency> |
这样在项目中就会有了 jackson-mapper-asl-1.9.11.jar 和 jackson-core-asl-1.9.11.jar 这两个 jar 包,其实当前版本是 2.1.0,只是 Maven 中央库中还没有。
上面代码结果是可预测了,就是:
1 2 3 4 5 6 |
{ "name" : "Unmi", "address" : { "province" : "JiangXi" } } |
Jackson 提供了 ObjectMapper 和 JsonGenerator 两个 API 让你去玩转 JSON,其实内部实现肯定汇合在一起了,别让两个选择就使你不会选择了。
如果把第一段代码中的第 20 行拿掉,运行会抛出异常:
Exception in thread "main" org.codehaus.jackson.map.JsonMappingException: No serializer found for class cc.unmi.testjackson.Person and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) )
at org.codehaus.jackson.map.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:52)
at org.codehaus.jackson.map.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:25)
at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:610)
at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:256)
at org.codehaus.jackson.map.ObjectMapper._configAndWriteValue(ObjectMapper.java:2575)
at org.codehaus.jackson.map.ObjectMapper.writeValueAsString(ObjectMapper.java:2097)
原因是 Jackson 默认情况下不知道怎么去序列化,解决办法是:
1. 用上面的第 20 行代码,setVisibility,这个属性也可以通过 SerializationConfig 来设置
2. 或是给你要转换 Json 的类加上注解 @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY)3. 也可以给字段或 getter 上加上 @JsonProperty 注解,还能用它指定序列化时的属性名
第 21 行是辅助设置,对格式的控制等,比要不要输出 null 值的,数字日期怎么显示,多瞧瞧那些 Feature:
org.codehaus.jackson.map.SerializationConfig.Feature.*
org.codehaus.jackson.JsonGenerator.Feature.*
org.codehaus.jackson.JsonParser.Feature.*
Jackson 的定制性还是很强的。再来想像这么一个需求,想输出的 JSON 属性也可定制,比如不想 {"name": "Unmi"},想要属性的第一个字母是大写的,{"Name": "Unmi"}。没问题,用 ObjectMapper 的
setPropertyNamingStrategy(PropertyNamingStrategy s) 方法
定义自己的 PropertyNamingStrategry, 可以继承自 PropertyNamingStrategy 或 PropertyNamingStrategyBase,显然继承自 PropertyNamingStrategyBase 来简单,因为它帮你适配了,只需覆写它的一个方法,如:
1 2 3 4 5 6 7 8 9 10 11 |
import org.codehaus.jackson.map.PropertyNamingStrategy.PropertyNamingStrategyBase; public class CapitalizedPropertyNamingStrategy extends PropertyNamingStrategyBase { @Override public String translate(String propertyName) { String name = propertyName.replaceAll("^\\w", propertyName.toUpperCase().substring(0,1)); return name; } } |
然后对 mapper 调用
1 |
mapper.setPropertyNamingStrategy(new CapitalizedPropertyNamingStrategy()); |
再次运行代码,结果就变成了:
1 2 3 4 5 6 |
{ "Name" : "Unmi", "Address" : { "Province" : "JiangXi" } } |
还有什么需求,混迹代码中,最不能缺的就是想像力了。
小结一下:
1. ObjectMapper 可用来序列化和反序列化对象,而 JsonParser 是用来反序列化的,JsonGenerator 是序列化的。若想让代码职责分明,就用 JsonParser 或 JsonGenerator 代替 ObjectMapper 来用
2. ObjectMapper 和 JsonGenerator 有各种各样的 writeXxx() 方法,可以让结果输出到不同的目的地; 同样 ObjectMapper 和 JsonParser 也有各色 readXxx() 方法,方便你从不同的源获得数据
3. 多看看 Jackson 为我们提供了注释 Annotation,留意上面那几个 Feature 中有些什么枚举值,这里面也是我们的扩展点。
参考: 1. JacksonInFiveMinutes
本文链接 https://yanbin.blog/jackson-java-object-json-string/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
http://godaddy.com/feedback/testing/social12
使用 Jackson 把 Java 对象转换成 JSON 字串 | 隔叶黄莺 Unmi Blog - 软件编程实践