标签:
POJO,Plain Old Java Object,是一种简单的Java对象,一般就是有一些private属性及其属性getter、setter方法的类。这种对象只能用来装载数据,作为数据存储的载体,而不具有业务逻辑处理的能力。
JSON,Javascript Object Notation,是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。其主要构建于两种结构:
之所以会把它们两者牵扯到一起,是因为互联网的产生带来了机器间通讯的需求,通常通过WebService提交和返回的结果都是JSON格式字符,但是在端(本文为Android端)中,则都需要以POJO为基础单位进行数据的装载。因此双方需要采用约定的协议,序列化和反序列化属于通讯协议的一部分。
假设Webservice返回了一个如下的Json序列化结果,代表用户排名信息:
{
‘name‘: ‘HuldaYu‘,
‘count‘: 11,
‘rank‘: 1
}
而其对应的POJO如下所示定义:
public class UserRank {
private int rank;
private String name;
private int count;
///setters & getters
}
那么我们可以通过将JSON序列化结果转成JsonObject,再写相应的Parser解析成我们需要的userRank
对象。
public class UserRankJSONParser {
public UserRank parse(JSONObject obj) {
UserRank rtn = new UserRank();
rtn.setRank(obj.optInt("rank", -1);
rtn.setName(obj.optString("name", "");
rtn.setCount(obj,optInt("count", -1));
return rtn;
}
}
同样的,若我们需要将一个userRank
对象序列化,则我们也需要再写一个对应的Composer组装成一个JsonObject,再转为Json串。
public class UserRankJSONComposer {
public JSONObject compose(UserRank model) {
if (model == null) return null;
JSONObject obj = new JSONObject();
obj.put("rank", model.getRank());
obj.put("name", model.getName());
obj.put("count", model.getCount());
return obj;
}
}
用这种方式,我们发现,我们需要为每个POJO实现parse和compose的方法,手工进行POJO与JSON之间的对应关系映射。
但是之前的这种手工的方式是否就可以满足我们了呢?
让我们这样来看之前的这种实现:如果需求变更,我们返回的json串中多了一个key需要解析呢?比如多了一个age
:
{
‘name‘: ‘HuldaYu‘,
‘count‘: 11,
‘rank‘: 1,
‘age‘: 17
}
那么对应的,我们的POJO中也需要增加age
属性:
public class UserRank {
// other properties
private int age;
}
Parser中也是:
public UserRank parse(JSONObject obj) {
UserRank rtn = new UserRank();
// other property parse
rtn.setAge(obj.optInt("age", -1));
return rtn;
}
同样的,POJO转成JSON的composer中也需要相应修改:
public JSONObject compose(UserRank model) {
// other property compose
obj.put("age", model.getAge());
return obj;
}
也就是说,如果有大量POJO改写变化,那么POJO本身的property要改,parser要改,composer也要改。只要有一处没有修改,就可能出现不一致性错误。同样,如果一个POJO有20个属性,则对应的parser和composer中也需要手工将这么多的属性进行手工映射处理。
那么这个过程是否可以自动化掉,从而减少代码量,减少出错可能?
仔细回想一开始想到的方法中,我们所做的工作,可以发现POJO中property的name就等于json中的key,property的value等于json的value;而json类型也与java类型有着映射关系:
No. | JSON类型 | Java类型 |
---|---|---|
1 | Object | LinkedHashMap<String, Object> |
2 | Array | ArrayList<Object> |
3 | String | String |
4 | Complete number | Integer, Long or BigInteger |
5 | Fractional number | Double/BigDecimal |
6 | True / False | Boolean |
7 | Null | Null |
而我们之前的parser和composer所做的只是纯手工进行对象映射。
但其实在Java这种动态语言中,我们是可以在运行时获取到类的property信息的,这就是Java反射机制。
Java反射是Java语言的一个很重要特性,它使得Java具体了“动态性”。这个机制使得程序在运行的时候可以获取任何一个已知名称的class的内部信息,包括其中的构造方法、声明的域和定义的方法等。只要有了java.lang.Class
类的对象,就可以通过getConstructor
、getField
和getMethod
方法来获取该类中的构造方法、域和方法。这三个方法还有相应的getDeclaredXXX
,其只会获取类本身所声明的元素,而不考虑继承下来的。
以前面的UserRank
为例:UserRank userRank = new UserRank();
有了Java反射机制和POJO与JSON中的自动映射关系,我们就可以用以下流程实现POJO的自动序列化过程:
根据POJO实例,遍历出POJO类的域,并自动转为JSON Object中的键值对。
因此,我们可以实现一个通用的第三方工具,在运行时环境下对标准POJO实现序列化与反序列化,而不需要在编码阶段确定POJO的对象类。同样也不需要像之前方案中的做法,在每个POJO中实现parse和compose方法,从而能有效提高开发效率,减少错误。此外,这样可以使得代码结构能加清晰简洁,方便日后扩展,也方便对更多的数据格式提供支持(如XML),只需要在工具中进行配置,不需要为每个POJO增加parseXML和composeXML等方法。
前面我们已经介绍了POJO序列化与反序列化的优化自动化方案,为了防止重复造轮子,我们先去看看是否已经有了第三方库支持。而实际上的确已经有了一些优秀的第三方库,例如Jackson、GSON、FastJson和Json-lib。
关于这些第三方库的性能对比测试等在网上有很多,大家都可以根据自己的需求去使用最合适的类库。而这里,我想简单的介绍下其中一款类库:Jackson。
Jackson主要提供了3个jar包:
还是以我们之前的UserRank
为例,利用Jackson的数据绑定,现在我们不需要额外写parse或compose方法,就可以简单的进行序列化和反序列化:
// 反序列化
ObjectMapper objectMapper = new ObjectMapper();
UserRank userRank = objectMapper.readValue(jsonStr, UserRank.class);
// 序列化
ObjectMapper objectMapper = new ObjectMapper();
String strObj = objectMapper.writeValueAsString(userRank);
此外,如果我们的POJO或者JSON并不规范,或者JSON中的key都是下划线命名,而POJO的属性都是驼峰命名,那么Jackson将不能直接解析出两者对应关系。
这时就可以用Jackson提供的注解功能了。常用的如下:
@JsonIgnoreProperties({"uselessValue"})
,或者直接忽略掉从JSON中获得的所有“多余”属性@JsonIgnoreProperties(ignoreUnknown=true)
属性别名
@JsonProperty("done_page_count")
private int donePageCount;
处理多态类型
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.WRAPPER_OBJECT, property="type")
@JsonSubTypes({
@JsonSubTypes.Type(value=Dog.class, name="dog"),
@JsonSubTypes.Type(value=Cat.class, name="cat")
})
本文从一开始的POJO和JSON的手工序列化和反序列化的方案中,分析其自动化可行性,给出其可信性方案,并列出了已有的第三方类库,简单介绍了其中的Jackson。如此可以大大减少重复代码量和出错可能性。
MAY 少写代码的信念 BE WITH YOU~
标签:
原文地址:http://blog.csdn.net/baidu_zhongce/article/details/51265175