JSON常用类库介绍(一)
JSON是一种文本方式展示结构化数据的方式,从产生开始就由于其简单好用、跨平台,特别适合HTTP下数据的传输(例如现在很流行的REST)而被广泛使用。
一、JSON是什么
JSON是一种文本方式展示结构化数据的方式,从产生开始就由于其简单好用、跨平台,特别适合HTTP下数据的传输(例如现在很流行的REST)而被广泛使用。
结构与类型
- 两种结构:对象内的键值对集合结构和数组,对象用{}表示、内部是”key”:”value”,数组用[]表示,不同值用逗号分开
- 基本数值有7个: true / false / null / object / array / number / string
- 结构可以嵌套,进而可以用来表达复杂的数据
优点
- 基于纯文本,所以对于人类阅读是很友好的。
- 规范简单,所以容易处理,开箱即用,特别是JS类的ECMA脚本里是内建支持的,可以直接作为对象使用。
- 平台无关性,因为类型和结构都是平台无关的,而且好处理,容易实现不同语言的处理类库,可以作为多个不同异构系统之间的数据传输格式协议,特别是在HTTP/REST下的数据格式。
缺点
- 性能一般,文本表示的数据一般来说比二进制大得多,在数据传输上和解析处理上都要更影响性能。
- 缺乏schema,跟同是文本数据格式的XML比,在类型的严格性和丰富性上要差很多。
Google JSON风格指南
遵循好的设计与编码风格,能提前解决80%的问题:
- 英文版Google JSON Style Guide:https://google.github.io/styleguide/jsoncstyleguide.xml
- 中文版Google JSON风格指南:https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md
二、常用JSON库
Gson
项目地址:https://github.com/google/gson
Gson是目前功能最全的Json解析神器,Gson当初是为因应Google公司内部需求而由Google自行研发而来,但自从在2008年五月公开发布第一版后已被许多公司或用户应用。 Gson的应用主要为toJson与fromJson两个转换函数,无依赖,不需要额外的jar,能够直接跑在JDK上。 类里面只要有get和set方法,Gson完全可以实现复杂类型的json到bean或bean到json的转换,是JSON解析的神器。
FastJson
项目地址:https://github.com/alibaba/fastjson
Fastjson是一个Java语言编写的高性能的JSON处理器,由阿里巴巴公司开发。无依赖,不需要额外的jar,能够直接跑在JDK上。 FastJson在复杂类型的Bean转换Json上会出现一些问题,可能会出现引用的类型,导致Json转换出错(可通过禁止循环引用避免)。 FastJson采用独创的算法,将parse的速度提升到极致,超过所有json库。
Jackson
项目地址:https://github.com/FasterXML/jackson
Jackson是当前用的比较广泛的,用来序列化和反序列化json的Java开源框架。Jackson社区比较活跃,更新速度也比较快, 从Github中的统计来看,Jackson是最流行的json解析器之一,Spring MVC的默认json解析器便是Jackson。Jackson 所依赖的jar包较少,简单易用,解析大的 json 文件速度比较快。
Json-lib
项目地址:http://json-lib.sourceforge.net/index.html
json-lib是最早的也是应用广泛的json解析工具,json-lib 不好的地方是依赖于很多第三方包,对于复杂类型的转换,json-lib对于json转换成bean还有缺陷, 比如一个类里面会出现另一个类的list或者map集合,json-lib从json到bean的转换就会出现问题。json-lib在功能和性能上面都不能满足现在互联网化的需求。
序列化性能对比:
反序列化性能对比:
从上面的测试结果对比可以看出来,Fastjson速度是真的牛,没有对手。Gson在序列化次数较少时性能还不错,次数多了以后相比Fastjson、Jackson速度稍微慢些,Jackson一直表现优异,至于Json-lib大家自己看吧。
三、Fastjson API
POJO与JSON转换
@Data
public class Sku implements Serializable {
/** id 编号 */
private long skuId;
/** 图片地址 */
private String imgUrl;
/** 创建时间 */
private Date createTime;
}
POJO序列化生成JSON:
@Test
public void testToJson() {
Sku sku = new Sku();
sku.setSkuId(20190324001L);
sku.setImgUrl("//img.com/fastjson.jpg");
sku.setCreateTime(new Date());
String json = JSON.toJSONString(sku);
// {"createTime":1553571270775,"imgUrl":"//img.com/fastjson.jpg","skuId":20190324001}
}
JSON反序列化生成POJO:
@Test
public void testFromJson() {
String jsonStr = "{\"createTime\":1553571270775,\"imgUrl\":\"//img.com/fastjson.jpg\",\"skuId\":20190324001}";
Sku sku = JSON.parseObject(jsonStr, Sku.class);
}
JSONObject、JSONArray与JSON转换
String jsonStr = "{\"createTime\":1553571270775,\"imgUrl\":\"//img.com/fastjson.jpg\",\"skuId\":20190324001}";
JSONObject jsonObject = JSON.parseObject(jsonStr);
jsonObject.getLong("skuId");
jsonObject.getString("imgUrl");
jsonObject.getDate("createTime");
...
String skuListJson = "[{\"skuId\":1,\"imgUrl\":\"//img.com/1.jpg\"},{\"skuId\":2,\"imgUrl\":\"//img.com/2.jpg\"}]";
JSONArray jsonArray = JSON.parseArray(skuListJson);
for (Object obj : jsonArray) {
JSONObject jsonObject = (JSONObject) obj;
jsonObject.getLong("skuId");
...
}
泛型反序列化
@Test
public void testTypeReference() {
String skuListJson = "[{\"skuId\":1,\"imgUrl\":\"//img.com/1.jpg\"},{\"skuId\":2,\"imgUrl\":\"//img.com/2.jpg\"}]";
List<Sku> skuList = JSON.parseObject(skuListJson, new TypeReference<List<Sku>>(){});
log.info(skuList.get(0).getImgUrl());
}
处理日期
指定日期输出格式:
JSON.toJSONStringWithDateFormat(sku, "yyyy-MM-dd HH:mm:ss");
// {"createTime":"2019-03-27 15:09:25","imgUrl":"//img.com/fastjson.jpg","skuId":20190324001}
使用ISO-8601日期格式:
JSON.toJSONString(sku, SerializerFeature.UseISO8601DateFormat);
// {"createTime":"2019-03-27T15:10:39.298+08:00","imgUrl":"//img.com/fastjson.jpg","skuId":20190324001}
全局修改日期格式:
JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd";
JSON.toJSONString(sku, SerializerFeature.WriteDateUseDateFormat);
// {"createTime":"2019-03-27","imgUrl":"//img.com/fastjson.jpg","skuId":20190324001}
反序列化能够自动识别常见日期格式:
ISO-8601日期格式
yyyy-MM-dd
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd HH:mm:ss.SSS
毫秒数字
毫秒数字字符串
定制序列化
SerializerFeature对Json格式定制
名称 | 含义 |
---|---|
QuoteFieldNames | 输出key时是否使用双引号,默认为true |
UseSingleQuotes | 使用单引号而不是双引号,默认为false |
WriteMapNullValue | 是否输出值为null的字段,默认为false |
WriteEnumUsingToString | Enum输出name()或者original,默认为false |
UseISO8601DateFormat | Date使用ISO8601格式输出,默认为false |
WriteNullListAsEmpty | List字段如果为null,输出为[],而非null |
WriteNullStringAsEmpty | 字符类型字段如果为null,输出为”“,而非null |
WriteNullNumberAsZero | 数值字段如果为null,输出为0,而非null |
WriteNullBooleanAsFalse | Boolean字段如果为null,输出为false,而非null |
SkipTransientField | 如果是true,类中的Get方法对应的Field是transient,序列化时将会被忽略。默认为true |
SortField | 按字段名称排序后输出。默认为false |
WriteTabAsSpecial | 把\t做转义输出,默认为false不推荐设为true |
PrettyFormat | 结果是否格式化,默认为false |
WriteClassName | 序列化时写入类型信息,默认为false |
DisableCircularReferenceDetect | 消除对同一对象循环引用的问题,默认为false |
WriteSlashAsSpecial | 对斜杠’/’进行转义 |
BrowserCompatible | 将中文都会序列化为\uXXXX格式,字节数会多一些,但是能兼容IE 6,默认为false |
WriteDateUseDateFormat | 全局修改日期格式,默认为false。 |
DisableCheckSpecialChar | 一个对象的字符串属性中如果有特殊字符如双引号,将会在转成json时带有反斜杠转移符。如果不需要转义,可以使用这个属性。默认为false |
BeanToArray | 将对象转为array输出 |
JSONField注解配置
- ordinal() 配置序列化和反序列化的顺序(默认按字母顺序序列化);
- name() 指定字段序列化的名称;
- format() 指定日期格式;
- serialize() deserialize() 是否序列化和反序列化,默认true;
- label() 打标记,可定制化输出
JSON.toJSONString(sku, Labels.includes("include"));
JSON.toJSONString(sku, Labels.excludes("exclude"));
- jsonDirect() 直接输出而不经过转译(对于存放json数据的string不做转义处理)
- serializeUsing() deserializeUsing() 指定序列化、反序列化使用自定义Serialize、Parser
public static class Model {
@JSONField(serializeUsing = ModelValueSerializer.class)
public int value;
}
public static class ModelValueSerializer implements ObjectSerializer {
@Override
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType,
int features) throws IOException {
Integer value = (Integer) object;
String text = value + "元";
serializer.write(text);
}
}
Model model = new Model();
model.value = 100;
String json = JSON.toJSONString(model); // {"value":"100元"}
- alternateNames() 别名,允许多个名字的变量转成同一个属性
- unwrapped() 不封箱
JSONType注解
JSONType和JSONField类似,但JSONType配置在类上,而不是field或者getter/setter方法上。不再详细介绍了。
SerializeFilter
SerializeFilter是通过编程扩展的方式定制序列化。fastjson支持多种SerializeFilter,用于不同场景的定制序列化。
- PropertyFilter 根据PropertyName和PropertyValue来判断是否序列化
- PropertyPreFilter 根据PropertyName判断是否序列化
- NameFilter 修改Key,如果需要修改Key,process返回值则可
- ValueFilter 修改Value
- BeforeFilter 序列化时在最前添加内容
- AfterFilter 序列化时在最后添加内容
1. PropertyFilter 根据PropertyName和PropertyValue来判断是否序列化
可以通过扩展实现根据object或者属性名称或者属性值进行判断是否需要序列化
public interface PropertyFilter extends SerializeFilter {
boolean apply(Object object, String propertyName, Object propertyValue);
}
2. PropertyPreFilter 根据PropertyName判断是否序列化
public interface PropertyPreFilter extends SerializeFilter {
boolean apply(JSONSerializer serializer, Object object, String name);
}
3. NameFilter 序列化时修改Key
如果需要修改Key,process返回值则可
public interface NameFilter extends SerializeFilter {
String process(Object object, String propertyName, Object propertyValue);
}
fastjson内置一个PascalNameFilter,用于输出将首字符大写的Pascal风格。 例如:
import com.alibaba.fastjson.serializer.PascalNameFilter;
Object obj = ...;
String jsonStr = JSON.toJSONString(obj, new PascalNameFilter());
4. ValueFilter 序列化时修改Value
public interface ValueFilter extends SerializeFilter {
Object process(Object object, String propertyName, Object propertyValue);
}
5. BeforeFilter 序列化时在最前添加内容
在序列化对象的所有属性之前执行某些操作,例如调用 writeKeyValue 添加内容
public abstract class BeforeFilter implements SerializeFilter {
protected final void writeKeyValue(String key, Object value) { ... }
// 需要实现的抽象方法,在实现中调用writeKeyValue添加内容
public abstract void writeBefore(Object object);
}
6. AfterFilter 序列化时在最后添加内容
在序列化对象的所有属性之后执行某些操作,例如调用 writeKeyValue 添加内容
public abstract class AfterFilter implements SerializeFilter {
protected final void writeKeyValue(String key, Object value) { ... }
// 需要实现的抽象方法,在实现中调用writeKeyValue添加内容
public abstract void writeAfter(Object object);
}
ParseProcess
ParseProcess是编程扩展定制反序列化的接口。fastjson支持如下ParseProcess:
- ExtraProcessor 用于处理多余的字段
- ExtraTypeProvider 用于处理多余字段时提供类型信息
1. 使用ExtraProcessor 处理多余字段
public static class VO {
private int id;
private Map<String, Object> attributes = new HashMap<String, Object>();
public int getId() { return id; }
public void setId(int id) { this.id = id;}
public Map<String, Object> getAttributes() { return attributes;}
}
ExtraProcessor processor = new ExtraProcessor() {
public void processExtra(Object object, String key, Object value) {
VO vo = (VO) object;
vo.getAttributes().put(key, value);
}
};
VO vo = JSON.parseObject("{\"id\":123,\"name\":\"abc\"}", VO.class, processor);
Assert.assertEquals(123, vo.getId());
Assert.assertEquals("abc", vo.getAttributes().get("name"));
2. 使用ExtraTypeProvider 为多余的字段提供类型
public static class VO {
private int id;
private Map<String, Object> attributes = new HashMap<String, Object>();
public int getId() { return id; }
public void setId(int id) { this.id = id;}
public Map<String, Object> getAttributes() { return attributes;}
}
class MyExtraProcessor implements ExtraProcessor, ExtraTypeProvider {
public void processExtra(Object object, String key, Object value) {
VO vo = (VO) object;
vo.getAttributes().put(key, value);
}
public Type getExtraType(Object object, String key) {
if ("value".equals(key)) {
return int.class;
}
return null;
}
};
ExtraProcessor processor = new MyExtraProcessor();
VO vo = JSON.parseObject("{\"id\":123,\"value\":\"123456\"}", VO.class, processor);
Assert.assertEquals(123, vo.getId());
Assert.assertEquals(123456, vo.getAttributes().get("value")); // value本应该是字符串类型的,通过getExtraType的处理变成Integer类型了。
更多高级功能
处理超大JSON文本,使用Stream API:https://github.com/alibaba/fastjson/wiki/Stream-api
JSONPath介绍:https://github.com/alibaba/fastjson/wiki/JSONPath