欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

全解史上最快的JOSN解析库alibaba Fastjson

程序员文章站 2022-03-13 22:33:43
前言json,全称:javascript object notation,作为一个常见的轻量级的数据交换格式,应该在一个程序员的开发生涯中是常接触的。简洁和清晰的层次结构使得 json 成为理想的数据...

前言

json,全称:javascript object notation,作为一个常见的轻量级的数据交换格式,应该在一个程序员的开发生涯中是常接触的。简洁和清晰的层次结构使得 json 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

java是面向对象的语言,所以我们更多的在项目中是以对象的形式处理业务的,但是在传输的时候我们却要将对象转换为 json 格式便于传输,而且 json 格式一般能解析为大多数的对象格式,而不在乎编程语言。

现在主流的对象与 json 互转的工具很多,我们主要介绍今天的主角,阿里巴巴的开源库 - fastjson。fastjson是一个java库,可用于将java对象转换为其json表示。它还可用于将json字符串转换为等效的java对象。fastjson可以处理任意java对象,包括您没有源代码的预先存在的对象。

全解史上最快的JOSN解析库alibaba Fastjson

什么是 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的资料请关注其它相关文章!