JSON解析类库之Fastjson(1) --- Fastjson基础知识、简单JavaBean与复杂Bean对象与JSON互转


-- Fastjson类库学习, 生成与解析json数据,json字符串与Java对象互转


在日志解析、前后端数据传输交互中,经常会遇到 String 与 map、json、xml 等格式相互转换与解析的场景,其中 json 基本成为了跨语言、跨前后端的事实上的标准数据交互格式。应该来说各个语言中解析 json 的库都一大片(具体 json 格式与三方库的介绍请见: http://www.json.org/json-zh.html ),比如 python 都集成在了内置库中,成为标准 API,今天我们要聊的是 java 中如何方便的使用 json 格式。


从上面的链接介绍中我们可以看到,java 的三方 json 库是最多的,可谓百花齐放百家争鸣的节奏。。。但是,某些库存在链式依赖的问题,使用的时候要注意,如果你没用 maven 管你你的依赖,用起来会很痛苦,比如 json-lib、smart-json 等。下面我要推荐的是阿里巴巴公司开源的一个 json 库:Fastjson,这个库在解析速度易用性上来说都很优秀。  

1、Fastjson简介:


Fastjson是一个Java语言编写的高性能功能完善的JSON库(JSON处理器,解析类库)。


1、遵循http://json.org标准,为其官方网站收录的参考实现之一。


2、功能强大,支持JDK的各种类型,包括基本类型、JavaBean、Collection、Map、Date、Enum、泛型等。


3、无依赖,不需要例外额外的jar,能够直接跑在JDK上。



4、开源,使用Apache License 2.0协议开源。


5、测试充分,fastjson有超过1500个testcase,每次构建都会跑一遍,丰富的测试场景保证了功能稳定。


官方站点:https://github.com/Alibaba/fastjson/wiki/%E9%A6%96%E9%A1%B5

Fastjson采用独创的算法,将parse的速度提升到极致,超过所有json库,包括曾经号称最快的jackson。并且还超越了 google的二进制协议protocol buf。Fastjson完全支持http://json.org的标准,也是官方网站收录的参考实现之一。支持各 种JDK类型。包括基本类型、JavaBean、Collection、Map、Enum、泛型等。支持JDK 5、JDK 6、Android、阿里云手机等环境。


阿里巴巴FastJson是一个Json处理工具包,包括“序列化”和“反序列化”两部分,它具备如下特征:



速度最快,测试表明,fastjson具有极快的性能,超越任其他的Java Json parser。包括自称最快的Jackson



功能强大,完全支持Java Bean、集合、Map、日期、Enum,支持范型,支持自省;无依赖,能够直接运行在Java SE 5.0以上版本;支持Android;开源 (Apache 2.0)


如何获得Fastjson?


你可以通过如下地方下载fastjson:


maven中央仓库: http://central.maven.org/maven2/com/alibaba/fastjson/


Sourceforge.net


在maven中如何配置fastjson依赖 :

fastjson最新版本都会发布到maven中央仓库,你可以直接依赖。

如果没有使用maven,可以直接下载


http://repo1.maven.org/maven2/com/alibaba/fastjson/   【包含各个版本,下载快】


https://github.com/alibaba/fastjson   【源码发布地方】

https://github.com/alibaba/fastjson/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98 【官方使用文档,中文版】 


版本建议:建议使用1.2.28及其以后的版本,之前的1.2.24之前的版本说是有个小漏洞,之后的版本已经修复了。


2. fastjson主要的API哪些?

fastjson入口类是com.alibaba.fastjson.JSON,主要的API是JSON.toJSONString,和parseObject。


package com.alibaba.fastjson;
public abstract class JSON {
      public static final String toJSONString(Object object);
      public static final <T> T parseObject(String text, Class<T> clazz, Feature... features);
}



序列化:

基本序列化

序列化就是把JavaBean对象转化成JSON格式的文本。



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的一些高级特性:



使用单引号:



标准的JSON是使用双引号的,javascript支持使用单引号格式的json文本,fastjson也支持这个特性,打开SerializerFeature.UseSingleQuotes这个特性就可以了了,例如:



Object o = ...;



String text = JSON.toJSONString(o, SerializerFeature.UseSingleQuotes);



fastjson序列化时可以选择的SerializerFeature有十几个,你可以按照自己的需要去选择使用。


日期格式化



fastjson直接支持日期类型数据的格式化,包括java.util.Date、java.sql.Date、java.sql.Timestamp、java.sql.Time。



默认情况下,fastjson将Date类型序列化为long,这个使得序列化和反序列化的过程不会导致时区问题。如:



例如:



long  millis = 1324138987429L;



Date date = new  Date(millis);        



System.out.println(JSON.toJSONString(date));



输出的结果是:



1324138987429


fastjson还提供了基于格式化输出的SerializerFeature,例如:



JSON.toJSONString(date, SerializerFeature.WriteDateUseDateFormat);



输出的结果为:



"2011-12-18 00:23:07"



你可以指定输出日期的格式,比如修改为输出毫秒:



JSON.toJSONStringWithDateFormat(date, "yyyy-MM-dd HH:mm:ss.SSS");



输出的结果为:



"2011-12-18 00:23:07.429"

使用WriteClassName特性



fastjson支持序列化时写入类型信息,从而使得反序列化时不至于类型信息丢失。例如:



Color color = Color.RED;



String text = JSON. toJSONString(color,  SerializerFeature. WriteClassName);



System.out.println(text);



输出结果:



{"@type":"java.awt.Color","r":255,"g":0,"b":0,"alpha":255}



由于序列化带了类型信息,使得反序列化时能够自动进行类型识别,例如:



String text = ...; // {"@type":"java.awt.Color","r":255,"g":0,"b":0,"alpha":255}



Color color = (Color) JSON.parse(text);


浏览器和设备兼容



fastjson默认的序列化内容,是对序列化结果紧凑做了优化配置,使得序列化之后长度更小,但是这种优化配置是对一些浏览器和设备不兼容的。比如说在iphone上兼容emoji(绘文字)。



JSON.toJSONString(o, SerializerFeature.BrowserCompatible);

循环引用



很多场景中, 我们需要序列化的对象中存在循环引用,在许多的json库中,这会导致stackoverflow。在功能强大的fastjson中,你不需要担心这个问题。例如:


A a = new  A();
 
       
B b = new  B(a);
 
       
a.setB(b);
  
       
String text = JSON.toJSONString(a); // {"b":{"a":{"$ref":".."}}}
 
       
A a1 = JSON.parseObject(text, A.class);
 
       
Assert.assertTrue(a1 == a1.getB().getA());



引用是通过"$ref"来表示的



引用 描述

"$ref":".."

上一级

"$ref":"@"

当前对象,也就是自引用

"$ref":"$"

根对象

"$ref":"$.children.0"

基于路径的引用,相当于 root.getChildren().get(0)



使用@JSONField


在某些场景,你可能需要定制序列化输出,比如说,希望序列化之后采用"ID",而不是"id",你可以使用@JSONField这个Annotation。

public class User {
@JSONField(name="ID")
    public int getId() { ... }
}
User user = ...;

JSON.toJSONString(user); // {"ID":234}


从json-lib中升级


如果你已经使用了json-lib,并且痛恨他蜗牛般的速度和罗嗦的API,建议你升级为fastjson,fastjson可以完全兼容json-lib的序列化格式。

import  com.alibaba.fastjson.JSON;
import  com.alibaba.fastjson.serializer.JSONLibDataFormatSerializer;
import  com.alibaba.fastjson.serializer.JSONSerializerMap;
import  com.alibaba.fastjson.serializer.SerializerFeature;
  
private static final SerializeConfig  config;
static  {
    config = new  SerializeConfig();
    config.put(java.util.Date.class, new JSONLibDataFormatSerializer()); // 使用和json-lib兼容的日期输出格式
  
private  static  final  SerializerFeature[] features = {
SerializerFeature.WriteMapNullValue, // 输出空置字段
        SerializerFeature.WriteNullListAsEmpty, // list字段如果为null,输出为[],而不是null
        SerializerFeature.WriteNullNumberAsZero, // 数值字段如果为null,输出为0,而不是null
        SerializerFeature.WriteNullBooleanAsFalse, // Boolean字段如果为null,输出为false,而不是null
        SerializerFeature.WriteNullStringAsEmpty // 字符类型字段如果为null,输出为"",而不是null
  };
  
// 序列化为和JSON-LIB兼容的字符串
public static String toCompatibleJSONString(Object object) {
    return  JSON.toJSONString(object, config, features);

}

通过上面代码中的toCompatibleJSONString方法,你就可以实现完全兼容json-lib了。


反序列化

反序列化就是把JSON格式的文本转化为Java Bean对象。


指定Class信息反序列化

通过指定类型信息,可以很方便的将"JSON文本"反序列化为"Java Bean"对象,例如:

String text = ...; // {"r":255,"g":0,"b":0,"alpha":255}

Color color = JSON.parseObject(text, Color.class);


类型集合的反序列化

这个接口类似于parseObject

String text = ...; // [{ ... }, { ... }]

List<User> users = JSON.parseArray(text, User.class);


泛型的反序列化

如果你需要返回一个带泛型的对象,例如List<User>、Map<String, User>,你可以使用TypeReference来传入类型信息。

String text = ...; // {"name":{"name":"ljw",age:18}}

Map<String, User> userMap = JSON.parseObject(text, new TypeReference<Map<String, User>>(){});


组合类型集合的反序列化

比如在网络协议中,经常会存在这样的组合:

[{/*header*/}, {/*body*/}]

fastjson对这种结构的反序列化有专门支持。

String text = ...; // [{/* header */}, {/* body */}]

Type[] types = new  Type[]{Header.class, Body.class};

List<Object> list = JSON.parseArray(text, types);

Header header = (Header) list.get(0);

Body body = (Body) list.get(1);


使用@JSONCreator来指定构造函数来创建对象

如果你的JavaBean没有缺省构造函数,可以使用@JSONCreator来指定构造函数

public static class Entity {
    private  final  int     id; //应该用Integer才符合规范
    private  final  String name;
 
  @JSONCreator
    public  Entity(@JSONField(name = "id") int  id, @JSONField(name = "name") String name){
        this.id = id;
        this.name = name;
    }
 
    public  int  getId() { return  id; }
    public  String getName() { return  name; }
 
}


把JSON文本反序列化为一个原型接口

public static interface Bean { //接口
    int  getId();
    void  setId(int  value);
    String getName();
    void  setName(String value);
}
 
String text = "{\"id\":123, \"name\":\"chris\"}";
Bean bean = JSON.parseObject(text, Bean.class);
 
// 按接口调用
Assert.assertEquals(123, bean.getId());
Assert.assertEquals("chris", bean.getName());
bean.setId(234);
 
Assert.assertEquals(234, bean.getId());


。。。未完,待续