全解史上最快的JOSN解析库alibaba Fastjson
前言
json,全称:javascript object notation,作为一个常见的轻量级的数据交换格式,应该在一个程序员的开发生涯中是常接触的。简洁和清晰的层次结构使得 json 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
java是面向对象的语言,所以我们更多的在项目中是以对象的形式处理业务的,但是在传输的时候我们却要将对象转换为 json 格式便于传输,而且 json 格式一般能解析为大多数的对象格式,而不在乎编程语言。
现在主流的对象与 json 互转的工具很多,我们主要介绍今天的主角,阿里巴巴的开源库 - fastjson。fastjson是一个java库,可用于将java对象转换为其json表示。它还可用于将json字符串转换为等效的java对象。fastjson可以处理任意java对象,包括您没有源代码的预先存在的对象。
什么是 fastjson?
阿里官方给的定义是, fastjson 是阿里巴巴的开源json解析库,它可以解析 json 格式的字符串,支持将 java bean 序列化为 json 字符串,也可以从 json 字符串反序列化到 javabean。
fastjson 的优点
- 速度快 fastjson相对其他json库的特点是快,从2011年fastjson发布1.1.x版本之后,其性能从未被其他java实现的json库超越。
- 使用广泛 fastjson在阿里巴巴大规模使用,在数万台服务器上部署,fastjson在业界被广泛接受。在2012年被开源中国评选为最受欢迎的国产开源软件之一。
- 测试完备 fastjson有非常多的testcase,在1.2.11版本中,testcase超过3321个。每次发布都会进行回归测试,保证质量稳定。
- 使用简单 fastjson的 api 十分简洁。
- 功能完备 支持泛型,支持流处理超大文本,支持枚举,支持序列化和反序列化扩展。
怎么获得 fastjson
你可以通过如下地方下载fastjson:
maven*仓库: http://central.maven.org/maven2/com/alibaba/fastjson/sourceforge.net : https://sourceforge.net/projects/fastjson/files/在maven项目的pom文件中直接配置fastjson依赖
fastjson最新版本都会发布到maven*仓库,你可以直接依赖。
<dependency> <groupid>com.alibaba</groupid> <artifactid>fastjson</artifactid> <version>x.x.x</version> </dependency>
其中x.x.x是版本号,根据需要使用特定版本,建议使用最新版本。
fastjson 主要的api
fastjson入口类是 com.alibaba.fastjson.json,主要的 api 是 json.tojsonstring 和 parseobject。
package com.alibaba.fastjson; public abstract class json { // java对象转换为json字符串 public static final string tojsonstring(object object); //json字符串转换为java对象 public static final <t> t parseobject(string text, class<t> clazz, feature... features); }
序列化:
string jsonstring = json.tojsonstring(obj);
反序列化:
vo vo = json.parseobject("...", vo.class);
泛型反序列化:
import com.alibaba.fastjson.typereference; list<vo> list = json.parseobject("...", new typereference<list<vo>>() {});
fastjson 的性能
fastjson是目前java语言中最快的json库,比自称最快的jackson速度还要快,第三方独立测试结果看这里:https://github.com/eishay/jvm-serializers/wiki。
自行做性能测试时,需关闭循环引用检测的功能。
json.tojsonstring(obj, serializerfeature.disablecircularreferencedetect) vo vo = json.parseobject("...", vo.class, feature.disablecircularreferencedetect)
另外,fastjson 比 gson 快大约6倍,测试结果可以看这里:
checking correctness…
[done]
pre-warmup… java-built-in hessian kryo protostuff-runtime avro-generic msgpack json/jackson/databind json/jackson/databind-strings json/jackson/db-afterburner json/google-gson/databind json/svenson-databind json/flexjson/databind json/fastjson/databind smile/jackson/databind smile/jackson/db-afterburner smile/protostuff-runtime bson/jackson/databind xml/xstream+c xml/jackson/databind-aalto
[done]
pre. create ser deser shal +deep total size +dfl
java-built-in 63 5523 27765 28084 28162 33686 889 514
hessian 64 3776 6459 6505 6690 10466 501 313
kryo 63 809 962 937 1001 1810 214 133
protostuff-runtime 62 671 903 920 957 1627 241 151
avro-generic 436 1234 1122 1416 1760 2994 221 133
msgpack 61 789 1369 1385 1449 2238 233 146
json/jackson/databind 60 1772 3089 3113 3246 5018 485 261
json/jackson/databind-strings 64 2346 3739 3791 3921 6267 485 261
json/jackson/db-afterburner 64 1482 2220 2233 2323 3805 485 261
json/google-gson/databind 64 7076 4894 4962 5000 12076 486 259
json/svenson-databind 64 5422 12387 12569 12468 17890 495 266
json/flexjson/databind 62 20923 26853 26873 27272 48195 503 273
json/fastjson/databind 63 1250 1208 1206 1247 2497 486 262
smile/jackson/databind 60 1697 2117 2290 2298 3996 338 241
smile/jackson/db-afterburner 60 1300 1614 1648 1703 3003 352 252
smile/protostuff-runtime 61 1275 1612 1638 1685 2961 335 235
bson/jackson/databind 63 5151 6729 6977 6918 12069 506 286
xml/xstreamc 62 6358 13208 13319 13516 19874 487 244
fastjson 使用示例
我们创建一个班级的对象,和一个学生对象如下:
班级对象
public class grade { private long id; private string name; private list<student> users = new arraylist<student>(); // 省略 setter、getter public void addstudent(student student) { users.add(student); } @override public string tostring() { return "grade{" + "id=" + id + ", name='" + name + '\'' + ", users=" + users + '}'; } }
学生对象
public class student { private long id; private string name; // 省略 setter、getter @override public string tostring() { return "student{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
运行的 main 函数
public class maintest { public static void main(string[] args) { grade group = new grade(); group.setid(0l); group.setname("admin"); student student = new student(); student.setid(2l); student.setname("guest"); student rootuser = new student(); rootuser.setid(3l); rootuser.setname("root"); group.addstudent(student); group.addstudent(rootuser); // 转换为 json string jsonstring = json.tojsonstring(group); system.out.println("json字符串:" + jsonstring); // 转换为 对象bean grade grade = json.parseobject(jsonstring, grade.class); system.out.println("javabean对象:" + grade); } }
最后的运行结果如下:
json字符串:
{"id":0,"name":"admin","users":[{"id":2,"name":"guest"},{"id":3,"name":"root"}]}
javabean对象:
grade{id=0, name='admin', users=[student{id=2, name='guest'}, student{id=3, name='root'}]}
将对象中的空值输出
在fastjson中,缺省是不输出空值的。无论map中的null和对象属性中的null,序列化的时候都会被忽略不输出,这样会减少产生文本的大小。但如果需要输出空值怎么做呢?
如果你需要输出空值,需要使用 serializerfeature.writemapnullvalue
model obj = ...; json.tojsonstring(obj, serializerfeature.writemapnullvalue);
几种空值特别处理方式:
serializerfeature | 描述 |
---|---|
writenulllistasempty | 将collection类型字段的字段空值输出为[] |
writenullstringasempty | 将字符串类型字段的空值输出为空字符串 "" |
writenullnumberaszero | 将数值类型字段的空值输出为0 |
writenullbooleanasfalse | 将boolean类型字段的空值输出为false |
具体的示例参考如下,可以同时选择多个:
class model { public list<objec> items; } model obj = ....; string text = json.tojsonstring(obj, serializerfeature.writemapnullvalue, serializerfeature.writenulllistasempty);
fastjson 处理日期
fastjson 处理日期的api很简单,例如:
json.tojsonstringwithdateformat(date, "yyyy-mm-dd hh:mm:ss.sss")
使用iso-8601日期格式
json.tojsonstring(obj, serializerfeature.useiso8601dateformat);
全局修改日期格式
json.deffault_date_format = "yyyy-mm-dd"; json.tojsonstring(obj, serializerfeature.writedateusedateformat);
反序列化能够自动识别如下日期格式:
- iso-8601日期格式
- yyyy-mm-dd
- yyyy-mm-dd hh:mm:ss
- yyyy-mm-dd hh:mm:ss.sss
- 毫秒数字
- 毫秒数字字符串
- .net json日期格式
- new date(198293238)
虽然上面处理了单个的日期类型和全局的日期类型格式的配置,但是有时候我们需要的是对象中个别的日期类型差异化,并不一定是同一种格式的。那如何处理呢?接下来介绍 fastjson 的定制序列化。
fastjson 定制序列化
fastjson支持多种方式定制序列化。
通过@jsonfield定制序列化通过@jsontype定制序列化通过serializefilter定制序列化通过parseprocess定制反序列化
使用@jsonfield配置
1、jsonfield 注解介绍
package com.alibaba.fastjson.annotation; public @interface jsonfield { // 配置序列化和反序列化的顺序,1.1.42版本之后才支持 int ordinal() default 0; // 指定字段的名称 string name() default ""; // 指定字段的格式,对日期格式有用 string format() default ""; // 是否序列化 boolean serialize() default true; // 是否反序列化 boolean deserialize() default true; }
2、jsonfield配置方式
可以把@jsonfield配置在字段或者getter/setter方法上,例如:
配置在字段上
public class vo { @jsonfield(name="id") private int id; @jsonfield(name="birthday",format="yyyy-mm-dd") public date date; }
配置在 getter/setter 上
public class vo { private int id; @jsonfield(name="id") public int getid() { return id;} @jsonfield(name="id") public void setid(int id) {this.id = id;} }
注意:若属性是私有的,必须有set*方法。否则无法反序列化。
3、使用format配置日期格式化
可以定制化配置各个日期字段的格式化
public class a { // 配置date序列化和反序列使用yyyymmdd日期格式 @jsonfield(format="yyyymmdd") public date date; }
4、使用serialize/deserialize指定字段不序列化
public class a { @jsonfield(serialize=false) public date date; } public class a { @jsonfield(deserialize=false) public date date; }
5、使用ordinal指定字段的顺序
缺省fastjson序列化一个java bean,是根据fieldname的字母序进行序列化的,你可以通过ordinal指定字段的顺序。这个特性需要1.1.42以上版本。
public static class vo { @jsonfield(ordinal = 3) private int f0; @jsonfield(ordinal = 2) private int f1; @jsonfield(ordinal = 1) private int f2; }
6、使用serializeusing制定属性的序列化类
在fastjson 1.2.16版本之后,jsonfield支持新的定制化配置serializeusing,可以单独对某一个类的某个属性定制序列化,比如:
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); assert.assertequals("{\"value\":\"100元\"}", json);
使用@jsontype配置
和jsonfield类似,但jsontype配置在类上,而不是field或者getter/setter方法上。
通过serializefilter定制序列化
1、简介
serializefilter是通过编程扩展的方式定制序列化。fastjson支持6种serializefilter,用于不同场景的定制序列化。
- propertyprefilter 根据propertyname判断是否序列化
- propertyfilter 根据propertyname和propertyvalue来判断是否序列化
- namefilter 修改key,如果需要修改key,process返回值则可
- valuefilter 修改value
- beforefilter 序列化时在最前添加内容
- afterfilter 序列化时在最后添加内容
2、propertyfilter根据propertyname和propertyvalue来判断是否序列化
public interface propertyfilter extends serializefilter { boolean apply(object object, string propertyname, object propertyvalue); }
可以通过扩展实现根据object或者属性名称或者属性值进行判断是否需要序列化。例如:
propertyfilter filter = new propertyfilter() { public boolean apply(object source, string name, object value) { if ("id".equals(name)) { int id = ((integer) value).intvalue(); return id >= 100; } return false; } }; json.tojsonstring(obj, filter); // 序列化的时候传入filter
3、propertyprefilter 根据propertyname判断是否序列化
和propertyfilter不同只根据object和name进行判断,在调用getter之前,这样避免了getter调用可能存在的异常。
public interface propertyprefilter extends serializefilter { boolean apply(jsonserializer serializer, object object, string name); }
4、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());
5、valuefilter 序列化时修改value
public interface valuefilter extends serializefilter { object process(object object, string propertyname, object propertyvalue); }
6、beforefilter 序列化时在最前添加内容
在序列化对象的所有属性之前执行某些操作,例如调用 writekeyvalue 添加内容
public abstract class beforefilter implements serializefilter { protected final void writekeyvalue(string key, object value) { ... } // 需要实现的抽象方法,在实现中调用writekeyvalue添加内容 public abstract void writebefore(object object); }
7、afterfilter 序列化时在最后添加内容
在序列化对象的所有属性之后执行某些操作,例如调用 writekeyvalue 添加内容
public abstract class afterfilter implements serializefilter { protected final void writekeyvalue(string key, object value) { ... } // 需要实现的抽象方法,在实现中调用writekeyvalue添加内容 public abstract void writeafter(object object); }
通过parseprocess定制反序列化
1、简介
parseprocess是编程扩展定制反序列化的接口。fastjson支持如下parseprocess:
- extraprocessor 用于处理多余的字段
- extratypeprovider 用于处理多余字段时提供类型信息
2、使用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"));
3、使用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类型了。
在 spring mvc 中集成 fastjson
如果你使用 spring mvc 来构建 web 应用并对性能有较高的要求的话,可以使用 fastjson 提供的fastjsonhttpmessageconverter 来替换 spring mvc 默认的 httpmessageconverter 以提高 @restcontroller @responsebody @requestbody 注解的 json序列化速度。下面是配置方式,非常简单。
xml式
如果是使用 xml 的方式配置 spring mvc 的话,只需在 spring mvc 的 xml 配置文件中加入下面配置即可
<mvc:annotation-driven> <mvc:message-converters> <bean class="com.alibaba.fastjson.support.spring.fastjsonhttpmessageconverter"/> </mvc:message-converters> </mvc:annotation-driven>
通常默认配置已经可以满足大部分使用场景,如果你想对它进行自定义配置的话,你可以添加 fastjsonconfig bean。
<mvc:annotation-driven> <mvc:message-converters> <bean class="com.alibaba.fastjson.support.spring.fastjsonhttpmessageconverter"> <property name="fastjsonconfig" ref="fastjsonconfig"/> </bean> </mvc:message-converters> </mvc:annotation-driven> <bean id="fastjsonconfig" class="com.alibaba.fastjson.support.config.fastjsonconfig"> <!-- 自定义配置... --> </bean>
编程式
如果是使用编程的方式(通常是基于 spring boot 项目)配置 spring mvc 的话只需继承 webmvcconfigureradapter 覆写configuremessageconverters方法即可,就像下面这样。
@configuration public class webmvcconfigurer extends webmvcconfigureradapter { @override public void configuremessageconverters(list<httpmessageconverter<?>> converters) { fastjsonhttpmessageconverter converter = new fastjsonhttpmessageconverter(); //自定义配置... //fastjsonconfig config = new fastjsonconfig(); //config.set ... //converter.setfastjsonconfig(config); converters.add(0, converter); } }
注意:
1、如果你使用的 fastjson 版本小于1.2.36的话(强烈建议使用最新版本),在与spring mvc 4.x 版本集成时需使用 fastjsonhttpmessageconverter4。
2、springboot 2.0.1版本中加载webmvcconfigurer的顺序发生了变动,故需使用converters.add(0, converter);指定fastjsonhttpmessageconverter在converters内的顺序,否则在springboot 2.0.1及之后的版本中将优先使用jackson处理。
在 spring data redis 中集成 fastjson
通常我们在 spring 中使用 redis 是通过 spring data redis 提供的 redistemplate 来进行的,如果你准备使用 json 作为对象序列/反序列化的方式并对序列化速度有较高的要求的话,建议使用 fastjson 提供的 genericfastjsonredisserializer 或 fastjsonredisserializer 作为 redistemplate 的 redisserializer。下面是配置方式,非常简单。
xml式
如果是使用 xml 的方式配置 spring data redis 的话,只需将 redistemplate 中的 serializer 替换为 genericfastjsonredisserializer 即可。
<bean id="redistemplate" class="org.springframework.data.redis.core.redistemplate"> <property name="connectionfactory" ref="jedisconnectionfactory"/> <property name="defaultserializer"> <bean class="com.alibaba.fastjson.support.spring.genericfastjsonredisserializer"/> </property> </bean>
下面是完整的 spring 集成 redis 配置供参考。
<!-- redis 连接池配置(可选) --> <bean id="jedispoolconfig" class="redis.clients.jedis.jedispoolconfig"> <property name="maxtotal" value="${redis.pool.maxactive}"/> <property name="maxidle" value="${redis.pool.maxidle}"/> <property name="maxwaitmillis" value="${redis.pool.maxwait}"/> <property name="testonborrow" value="${redis.pool.testonborrow}"/> <!-- 更多连接池配置...--> </bean> <!-- redis 连接工厂配置 --> <bean id="jedisconnectionfactory" class="org.springframework.data.redis.connection.jedis.jedisconnectionfactory"> <!--设置连接池配置,不设置的话会使用默认的连接池配置,若想禁用连接池可设置 usepool = false --> <property name="poolconfig" ref="jedispoolconfig" /> <property name="hostname" value="${host}"/> <property name="port" value="${port}"/> <property name="password" value="${password}"/> <property name="database" value="${database}"/> <!-- 更多连接工厂配置...--> </bean> <!-- redistemplate 配置 --> <bean id="redistemplate" class="org.springframework.data.redis.core.redistemplate"> <!-- 设置 redis 连接工厂--> <property name="connectionfactory" ref="jedisconnectionfactory"/> <!-- 设置默认 serializer ,包含 keyserializer & valueserializer --> <property name="defaultserializer"> <bean class="com.alibaba.fastjson.support.spring.genericfastjsonredisserializer"/> </property> <!-- 单独设置 keyserializer --> <property name="keyserializer"> <bean class="com.alibaba.fastjson.support.spring.genericfastjsonredisserializer"/> </property> <!-- 单独设置 valueserializer --> <property name="valueserializer"> <bean class="com.alibaba.fastjson.support.spring.genericfastjsonredisserializer"/> </property> </bean>
编程式
如果是使用编程的方式(通常是基于 spring boot 项目)配置 redistemplate 的话只需在你的配置类(被@configuration注解修饰的类)中显式创建 redistemplate bean,设置 serializer 即可。
@bean public redistemplate redistemplate(redisconnectionfactory redisconnectionfactory) { redistemplate redistemplate = new redistemplate(); redistemplate.setconnectionfactory(redisconnectionfactory); genericfastjsonredisserializer fastjsonredisserializer = new genericfastjsonredisserializer(); redistemplate.setdefaultserializer(fastjsonredisserializer);//设置默认的serialize,包含 keyserializer & valueserializer //redistemplate.setkeyserializer(fastjsonredisserializer);//单独设置keyserializer //redistemplate.setvalueserializer(fastjsonredisserializer);//单独设置valueserializer return redistemplate; }
通常使用 genericfastjsonredisserializer 即可满足大部分场景,如果你想定义特定类型专用的 redistemplate 可以使用 fastjsonredisserializer 来代替 genericfastjsonredisserializer ,配置是类似的。
以上就是全解史上最快的josn解析库alibaba fastjson的详细内容,更多关于josn解析库alibaba fastjson的资料请关注其它相关文章!
上一篇: python 使用csv模块读写csv格式文件的示例
下一篇: 如何试着在你的css增加粘稠效果