JSON 序列化与反序列化详解(上)

一、什么是JSON?

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming LanguageStandard ECMA-262 3rd Edition - December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语言。

JSON建构于两种结构:

· “名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),纪录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 (associative array)。

· 值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。

这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式在同样基于这些结构的编程语言之间交换成为可能。

JSON具有以下这些形式:

对象是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称/值’ 对”之间使用“,”(逗号)分隔。

224513_XK32_2505908.png

数组是值(value)的有序集合。一个数组以“[”(左中括号)开始,“]”(右中括号)结束。值之间使用“,”(逗号)分隔。

224533_OPDw_2505908.png

值(value)可以是双引号括起来的字符串(string)、数值(number)、true、false、 null、对象(object)或者数组(array)。这些结构可以嵌套。

224545_3Fl0_2505908.png

字符串(string)是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。

字符串(string)与C或者Java的字符串非常相似。

224601_7cPx_2505908.png

数值(number)也与C或者Java的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。

224619_fWhy_2505908.png

空白可以加入到任何符号之间。 以下描述了完整的语言。

、fastjson简介:

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

1. 序列化

所谓序列化,就是将java各种对象转化为json串。不多说,先上图。

224741_20Tj_2505908.png

序列化入口

平常我们经常用到的是JSON.toJSONString()这个静态方法来实现序列化。其实JSON是一个抽象类,该类实现了JSONAware(转为json串)和JSONStreamAware(将json串写入Appendable中)的接口,同时又是JSONArray(内部实现就是个List)和JSONObject(内部实现就是个Map)的父类。JSON.toJSONString()方法内部实现基本相同,为做某些特定配置,对外暴露的接口可能不同。该方法的实现实际托付给了JSONSerializer类。

序列化组合器

JSONSerializer类相当于一个序列化组合器,它将上层调用、序列化配置、具体类型序列化实现、序列化字符串拼接等功能组合在一起,方便外部统一调用。该类有几个重要的成员,SerializeConfig、SerializeWriter、各种Filter列表、DateFormat、SerialContext等,还有每次对各个具体对象序列化的ObjectSerializer(非JSONSerializer的成员变量)。下面就来挨个说明其各自功能。

1.  SerializeConfig

SerializeConfig是全局唯一的,它继承自IdentityHashMap,IdentityHashMap是一个长度默认为1024的Hash桶,每个桶存放相同Hash的Entry(可看做链表节点,包含key、value、next指针、hash值)做成的单向链表,IdentityHashMap实现了HashMap的功能,但能避免HashMap并发时的死循环。  

SerializeConfig的主要功能是配置并记录每种Java类型对应的序列化类(ObjectSerializer接口的实现类),比如Boolean.class使用BooleanCodec(看命名就知道该类将序列化和反序列化实现写到一起了)作为序列化实现类,float[].class使用FloatArraySerializer作为序列化实现类。这些序列化实现类,有的是FastJSON中默认实现的(比如Java基本类),有的是通过ASM框架生成的(比如用户自定义类),有的甚至是用户自定义的序列化类(比如Date类型框架默认实现是转为毫秒,应用需要转为秒)。当然,这就涉及到是使用ASM生成序列化类还是使用JavaBean的序列化类类序列化的问题,这里判断根据就是是否Android环境(环境变量"java.vm.name"为"dalvik"或"lemur"就是Android环境),但判断不仅这里一处,后续还有更具体的判断。

2.  SerializeWriter

SerializeWriter继承自Java的Writer,其实就是个转为FastJSON而生的StringBuilder,完成高性能的字符串拼接。该类成员如下:

· char buf[]

 可理解为每次序列化后字符串的内存存放地址。

· static ThreadLocal> bufLocal 

每次序列化,都需要重新分配buf[]内存空间。而bufLocal就是每次序列化后bug[]的内存空间保留到ThreadLocal里,但其中的值清空,避免频繁的内存分配和gc。

· int features 

生成json字符串的特征配置,默认配置为: 

<span>QuoteFieldNames | SkipTransientField | WriteEnumUsingToString | SortField</span>

 表示含义为:双引号filedName and 忽略transientField and enum类型使用String写入 and 排序输出field。 支持的所有特征在SerializerFeature类中,用户可在调用时显示配置,也可通过JSONFiled或JSONType注入配置。

· Writer 

writer 用户指定将生成的json串直接写入某writer中,比如JSONWriter类。

举个例子吧,writeStringWithDoubleQuote()表示用字符串用双引号写入,看看如何拼接字符串的。

3.  Filter列表

SerializeWriter中有很多Filter列表,可视为在生成json串的各阶段、各地方定制序列化,大致如下:

· BeforeFilter :序列化时在最前面添加内容

· AfterFilter :序列化时在最后面添加内容

· PropertyFilter :根据PropertyName和PropertyValue来判断是否序列化

· ValueFilter :修改Value

· NameFilter :修改key

· PropertyPreFilter :根据PropertyName判断是否序列化

4.  DateFormat

指定日期格式。若不指定,FastJSON会自动识别如下日期格式:

· ISO-8601日期格式

· yyyy-MM-dd

· yyyy-MM-dd HH:mm:ss

· yyyy-MM-dd HH:mm:ss.SSS

· 毫秒数值

· 毫秒字符串

· .Net Json日期格式

· new Date()

5.  SerialContext

序列化上下文,在引用或循环引用中使用,该值会放入references的Hash桶(IdentityHashMap)缓存。

6.  ObjectSerializer  

ObjectSerializer只有一个接口方法,如下:

void write(JSONSerializer serializer,Objectobject,Object  fieldName,Type fieldType);

 

可见,将JSONSerializer传入了ObjectSerializer中,而JSONSerializer有SerializeWriter成员,在每个具体ObjectSerializer实现中,直接使用SerializeWriter拼接字符串即可;Object即是待序列化的对象;fieldName则主要用于组合类引用时设置序列化上下文;而fieldType主要是为了泛型处理。  

JSONSerializer中通过public ObjectSerializer getObjectWriter(Class clazz)函数获取类对应的序列化类(即实现ObjectSerializer接口的类),大致逻辑如下:

224818_EHSr_2505908.png

整个过程是先获取已实现基础类对应的序列化类,再通过类加载器获取自定义的AutowiredObjectSerializer序列化类,最后获取通过createJavaBeanSerializer()创建的序列化类。通过该方法会获取两种序列化类,一种是直接的JavaBeanSerializer(根据类的get方法、public filed等JavaBean特征序列化),另一种是createASMSerializer(通过ASM框架生成的序列化字节码),优先使用第二种。选择JavaBeanSerializer的条件为:

· 该clazz为非public类

· 该clazz的类加载器在ASMClassLoader的外部,或者clazz就是 Serializable.class,或者clazz就是Object.class

· JSONType的注解指明不适用ASM

· createASMSerializer加载失败 

结合前面的讨论,可以得出使用ASM的条件:非Android系统、非基础类、非自定义的AutowiredObjectSerializer、非以上所列的使用JavaBeanSerializer条件。 

具体基础类的序列化方法、JavaBeanSerializer的序列化方法和ASM生成的序列化方法可以参见代码,这里就不做一一讲解了。

2. 反序列化

所谓反序列化,就是将json串转化为对应的java对象。还是先上图。

同样是JSON类作为反序列化入口,实现了parse()、parseObject()、parseArray()等将json串转换为java对象的静态方法。这些方法的实现,实际托付给了DefaultJSONParser类。   

DefaultJSONParser类相当于序列化的JSONSerializer类,是个功能组合器,它将上层调用、反序列化配置、反序列化实现、词法解析等功能组合在一起,相当于设计模式中的外观模式,供外部统一调用。同样,我们来分析该类的几个重要成员,看看他是如何实现纷繁的反序列化功能的。

1.  ParserConfig

同SerializeConfig,该类也是全局唯一的解析配置,其中的boolean asmEnable同样判断是否为Andriod环境。与SerializeConfig不同的是,配置类和对应反序列类的IdentityHashMap是该类的私有成员,构造函数的时候就将基础反序列化类加载进入IdentityHashMap中。

2.  JSONLexer 

JSONLexer是个接口类,定义了各种当前状态和操作接口。JSONLexerBase是对JSONLexer实现的抽象类,类似于序列化的SerializeWriter类,专门解析json字符串,并做了很多优化。实际使用的是JSONLexerBase的两个子类JSONScanner和JSONLexerBase,前者是对整个字符串的反序列化,后者是接Reader直接序列化。简析JSONLexerBase的某些成员:

· int token

由于json串具有一定格式,字符串会根据某些特定的字符来自解释所表示的意义,那么这些特定的字符或所处位置的字符在FastJSON中就叫一个token,比如"(","{","[",",",":",key,value等,这些都定义在JSONToken类中。

· char[] sbuf

解析器通过扫描输入字符串,将匹配得到的最细粒度的key、value会放到sbuf中。

· static ThreadLocal> SBUF_REF_LOCAL

上面sbuf的空间不释放,在下次需要的时候直接拿出来使用,从避免的内存的频繁分配和gc。

· features

反序列化特性的配置,同序列化的feature是通过int的位或来实现其特性开启还是关闭的。默认配置是: AutoCloseSource | UseBigDecimal | AllowUnQuotedFieldNames | AllowSingleQuotes | AllowArbitraryCommas | AllowArbitraryCommas | SortFeidFastMatch | IgnoreNotMatch ,表示检查json串的完整性 and 转换数值使用BigDecimal and 允许接受不使用引号的filedName and 允许接受使用单引号的key和value and 允许接受连续多个","的json串 and 使用排序后的field做快速匹配 and 忽略不匹配的key/value对。当然,这些参数也是可以通过其他途径配置的。

· hasSpecial

对转义符的处理,比如'\0','\'等。

词法解析器是基于预测的算法从左到右一次遍历的。由于json串具有自身的特点,比如为key的token后最有可能是":",":"之后可能是value的token或为"{"的token或为"["的token等等,从而可以根据前一个token预判下一个token的可能,进而得知每个token的含义。分辨出各个token后,就可以获取具体值了,比如scanString获取key值,scanFieldString根据fieldName获取fieldValue,scanTrue获取java的true等等。其中,一般会对key进行缓存,放入SymbolTable(类似于IdentityHashMap)中,猜想这样做的目的是:应用解析的json串一般key就那么多,每次生成开销太多,干脆缓存着,用的就是就来取,还是空间换时间的技巧。

3.  List< ExtraTypeProvider >和List< ExtraProcessor >

视为对其他类型的处理和其他自定义处理而留的口子,用户可以自己实现对应接口即可。

4.  DateFormat

同序列化的DateFormat,不多说了。

5.  ParseContext 和 List< ResolveTask >

ParseContext同序列化的SerialContext,为引用甚至循环引用做准备。   

List< ResolveTask >当然就是处理这种多层次甚至多重引用记录的list了。

6.  SymbolTable

上面提到的key缓存。

7.  ObjectDeserializer

跟ObjectSerializer也是相似的。先根据fieldType获取已缓存的解析器,如果没有则根据fieldClass获取已缓存的解析器,否则根据注解的JSONType获取解析器,否则通过当前线程加载器加载的AutowiredObjectDeserializer查找解析器,否则判断是否为几种常用泛型(比如Collection、Map等),最后通过createJavaBeanDeserializer来创建对应的解析器。当然,这里又分为JavaBeanDeserializer和asmFactory.createJavaBeanDeserializer两种。使用asm的条件如下:

· 非Android系统

· 该类及其除Object之外的所有父类为是public的

· 泛型参数非空

· 非asmFactory加载器之外的加载器加载的类

· 非接口类

· 类的setter函数不大于200

· 类有默认构造函数

· 类不能含有仅有getter的filed

· 类不能含有非public的field

· 类不能含有非静态的成员类

· 类本身不是非静态的成员类

使用ASM生成的反序列化器具有较高的反序列化性能,比如对排序的json串可按顺序匹配解析,从而减少读取的token数,但如上要求也是蛮严格的。综上,FastJSON反序列化也支持基础反序列化器、JavaBeanDeserializer反序列化器和ASM构造的反序列化器,这里也不做一一讲解了。

3. Why So Fast

FastJSON真的很快,读后受益匪浅。个人总结了下快的原因(不一定完整):

1.  专业的心做专业的事

不论是序列化还是反序列化,FastJSON针对每种类型都有与之对应的序列化和反序列化方法,就针对这种类型来做,优化性能自然更具针对性。自编符合json的SerializeWriter和JSONLexer,就连ASM框架也给简化掉了,只保留所需部分。不得不叹其用心良苦。

2.  无处不在的缓存

空间换时间的想法为程序员屡试不爽,而作者将该方法用到任何细微之处:类对应的序列化器/反序列化器全部存起来,方便取用;解析的key存起来,表面重复内存分配等等。

3.  不厌其烦的重复代码

我不知道是否作者故意为之,程序中出现了很多类似的代码,比如特殊字符处理、不同函数对相同token的处理等。这样虽对于程序员寻求规整相违背,不过二进制代码却很喜欢,无形之中减少了许多函数调用。

4.  不走寻常路

对于JavaBean,可以通过发射实现序列化和反序列化(FastJSON已有实现),但默认使用的是ASM框架生成对应字节码。为了性能,无所不用其极。

5.  一点点改变有很大的差别

排序对输出仅是一点小小的改变,丝毫不影响json的使用,但却被作者用在了解析的快速匹配上,而不用挨个拎出key。

6.  从规律中找性能

上面也讲到,FastJSON读取token基于预测的。json串自身的规律性被作者逮个正着,预测下一个将出现的token处理比迷迷糊糊拿到一个token再分情况处理更快捷。

4.FastJson使用

接口介绍

Fastjson API入口类是com.alibaba.fastjson.JSON,常用的序列化操作都可以在JSON类上的静态方法直接完成。

l // 把JSON文本parse为JSONObject或者JSONArray 

public static final Object parse(String text); 

 

l  // 把JSON文本parse成JSONObject
public static final JSONObject parseObject(String text);   

 

l // 把JSON文本parse为JavaBean 
public static final <T> T parseObject(String text, Class<T> clazz); 

 

l // 把JSON文本parse成JSONArray 

public static final JSONArray parseArray(String text); 

 

l  //把JSON文本parse成JavaBean集合

public static final <T> List<T> parseArray(String text, Class<T> clazz); 

 

l  // 将JavaBean序列化为JSON文本 
public static final String toJSONString(Object object);

 

l  // 将JavaBean序列化为带格式的JSON文本 

public static final String toJSONString(Object object, boolean prettyFormat);

 

l //将JavaBean转换为JSONObject或者JSONArray。

public static final Object toJSON(Object javaObject);

有关类库说明

v SerializeWriter:相当于StringBuffer

 

v JSONArray:相当于List<Object>

 

v JSONObject:相当于Map<String, Object>

 

JSON反序列化没有真正数组,本质类型都是List<Object>

 

 

Fastjson序列化json字符串

JavaBean,

List<JavaBean>,

List<String>,

List<Map<String,Object>

      String jsonStrng = JSON.toJSONString(object);

fastJson反序列化json字符串为四种类型

      1. JavaBean

     Student student =  JSON.parseObject(jsonString, Student.class);

      2. List<JavaBean>

     List<Student> listPerson =JSON.parseArray(jsonString, Student.class);

      3. Map<String,Student>

    Map<String, Object> listPerson = JSON.parseObject(jsonString,new TypeReference<Map<String,Object>>(){});

      4. List<Map<String,Object>>

          List<Map<String, Object>> listMap = JSON.parseObject(jsonString, new TypeReference<List<Map<String,Object>>>(){}); 

 (注意:此处可看出fastjson反射机制比gson更准确, id = 1001 通过fastjson反射仍是 id = 1001 , 而通过gson反射结果 为 id =1001.0 ,)

实战例子

1)定义了JSONObject去解析

 {"JACKIE_ZHANG":"张学友","ANDY_LAU":"刘德华","LIMING":"黎明","Aaron_Kwok":"郭富城"}   

import com.alibaba.fastjson.JSONObject;

import java.util.Map;

 

public class FastJsonTest {
    public static void main(String[] args) {
        String jsonStr="{\"JACKIE_ZHANG\":\"张学友\",\"ANDY_LAU\":\"刘德华\",\"LIMING\":\"黎明\",\"Aaron_Kwok\":\"郭富城\"}";

        JSONObject jsonObject=JSONObject.parseObject(jsonStr);

        for(Map.Entry<String,Object>entry : jsonObject.entrySet()){
            System.out.print(entry.getKey()+"-"+entry.getValue()+"\t");
        }
    }
}


运行结果:

 

 

 

2)定义为JSONArray 解析出来之后是有序的

代码如下:

 

package com.jhaso.fastjson.test5;

import com.alibaba.fastjson.JSONArray;

/**
 * @author jhaso
 * @create 2017-07-14 10:32
 * @version: V1.0.0
 **/
public class FastJsonTest2 {
    public static void main(String[] args) {
        String jsonStr="[{\"JACKIE_ZHANG\":\"张学友\"},{\"ANDY_LAU\":\"刘德华\"},{\"LIMING\":\"黎明\"},{\"Aaron_Kwok\":\"郭富城\"}]";
        //做5次测试
        JSONArray jsonArray=JSONArray.parseArray(jsonStr);

        for(int k=0;k<jsonArray.size();k++){
            System.out.print(jsonArray.get(k)+"\t");
        }
        System.out.println();//用来换行
    }
}


运行结果为:

 

{"JACKIE_ZHANG":"张学友"} {"ANDY_LAU":"刘德华"}{"LIMING":"黎明"}{"Aaron_Kwok":"郭富城"}  

{"JACKIE_ZHANG":"张学友"} {"ANDY_LAU":"刘德华"}{"LIMING":"黎明"}{"Aaron_Kwok":"郭富城"}  

{"JACKIE_ZHANG":"张学友"} {"ANDY_LAU":"刘德华"}{"LIMING":"黎明"}{"Aaron_Kwok":"郭富城"}  

{"JACKIE_ZHANG":"张学友"} {"ANDY_LAU":"刘德华"}{"LIMING":"黎明"}{"Aaron_Kwok":"郭富城"}  

{"JACKIE_ZHANG":"张学友"} {"ANDY_LAU":"刘德华"}{"LIMING":"黎明"}{"Aaron_Kwok":"郭富城"}  

 

3)使用Gson的方法

如果就想要定义为JSONObject,而不是JSONArray,可以选用其他JSON解析器,个人推荐使用google的gson,文档明显比fastjson好很多(从这里可以看出阿里巴巴和谷歌的差距):

 

package com.jhaso.fastjson.test5;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

/**
 * @author 王帅兵
 * @create 2017-07-14 10:32
 * @version: V1.0.0
 **/
public class FastJsonTest3 {
    public static void main(String[] args) {
        String jsonStr = "{\"JACKIE_ZHANG\":\"张学友\",\"ANDY_LAU\":\"刘德华\",\"LIMING\":\"黎明\",\"Aaron_Kwok\":\"郭富城\"}";
        //做5次测试
        for (int i = 0, j = 5; i < j; i++) {
            JsonObject jsonObject = (JsonObject) new JsonParser().parse(jsonStr);
            for (java.util.Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
                System.out.print(entry.getKey() + "-" + entry.getValue() + "\t");
            }
            System.out.println();//用来换行
        }
    }
}


运行结果:

 

 

JACKIE_ZHANG-"张学友"  ANDY_LAU-"刘德华"  LIMING-"黎明" Aaron_Kwok-"郭富城"      

JACKIE_ZHANG-"张学友"  ANDY_LAU-"刘德华"  LIMING-"黎明" Aaron_Kwok-"郭富城"      

JACKIE_ZHANG-"张学友"  ANDY_LAU-"刘德华"  LIMING-"黎明" Aaron_Kwok-"郭富城"      

JACKIE_ZHANG-"张学友"  ANDY_LAU-"刘德华"  LIMING-"黎明" Aaron_Kwok-"郭富城"      

JACKIE_ZHANG-"张学友"  ANDY_LAU-"刘德华"  LIMING-"黎明" Aaron_Kwok-"郭富城" 

 

fastjson的定制序列化有很多中实现方式,主要有如下的方式:

1) 通过@JSONField定制序列化

2) 通过@JSONType定制序列化

3) 通过SerializeFilter定制序列化

fastJson 注解

@JSONField的定制序列化

 1. Field

 2. Setter 和 Getter方法

注:FastJson在进行操作时,是根据getter和setter的方法进行的,并不是依据Field进行。

 

1) 作用Field

       @JSONField作用在Field时,其name不仅定义了输入key的名称,同时也定义了输出的名称。代码如下:

 

package com.jhaso.fastjson.test6;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;

/**
 * @author jhaso
 * @create 2017-07-14 12:50
 * @version: V1.0.0
 **/
public class Person {

    @JSONField(name="n")
    private  String  name;

    @JSONField(name="a")
    private  String  age;

    @JSONField(name="d")
    private String desc;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    @Override
    public String toString() {
        return JSONObject.toJSONString(this);
    }

}

 

 

package com.jhaso.fastjson.test6;

/**
 * @author jhaso
 * @create 2017-07-14 12:54
 * @version: V1.0.0
 **/

import com.alibaba.fastjson.JSONObject;
import org.junit.Before;
import org.junit.Test;

public class PersonTest {

    private Person person;

    /**
     * 初始化对象
     */
    @Before
    public void setUp() {
        person = new Person();
        person.setName("jhaso");
        person.setAge("28");
        person.setDesc("just only test");
    }

    @Test
    public void test() {
        String jsonStr = JSONObject.toJSONString(person);
        System.out.println("bean -> json:" + jsonStr);

        //改变json的key为大写
        jsonStr = jsonStr.toUpperCase();

        System.out.println("需要转换的json:" + jsonStr);
        person = JSONObject.toJavaObject(JSONObject.parseObject(jsonStr), Person.class);
        System.out.println("json -> bean:" + person.getName());
    }
}

 

输出如下:


从上面我们可以看出,当@JSONField作用在Fileld上时,定义了输入和输出,如果我们传输过来的json格式不符合这个格式时,则不能够正确转换。

 

2) 作用在setter和getter方法上

 

顾名思义,当作用在setter方法上时,就相当于根据 name 到 json中寻找对应的值,并调用该setter对象赋值。

当作用在getter上时,在bean转换为json时,其key值为name定义的值。实例如下:

 

package com.jhaso.fastjson.test7;

import com.alibaba.fastjson.annotation.JSONField;

/**
 * @author jhaso
 * @create 2017-07-14 12:50
 * @version: V1.0.0
 **/
public class Product {

    private String productName;
    private String desc;
    private String price;

    @JSONField(name="n")
    public String getProductName() {
        return productName;
    }

    @JSONField(name="N")
    public void setProductName(String productName) {
        this.productName = productName;
    }

    @JSONField(name="D")
    public String getDesc() {
        return desc;
    }

    @JSONField(name="d")
    public void setDesc(String desc) {
        this.desc = desc;
    }

    @JSONField(name="PP")
    public String getPrice() {
        return price;
    }

    @JSONField(name="pp")
    public void setPrice(String price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Product : {" +
                "productName='" + this.getProductName() + '\'' +
                ", desc='" + desc + '\'' +
                ", price='" + this.getPrice() + '\'' +
                '}';
    }

}

package com.jhaso.fastjson.test7;

import com.alibaba.fastjson.JSONObject;

/**
 * @author 王帅兵
 * @create 2017-07-14 12:54
 * @version: V1.0.0
 **/

public class PersonTest {
    public static void main(String[] args) {

        Product product = new Product();
        product.setProductName("产品测试");
        product.setDesc("产品描述");
        product.setPrice("23.45");

        String jsonStr = JSONObject.toJSONString(product);
        System.out.println("转换为json:" + JSONObject.toJSONString(product));

       // jsonStr = jsonStr.toUpperCase();
       // System.out.println(jsonStr);

        product = JSONObject.toJavaObject(JSONObject.parseObject(jsonStr), Product.class);
        System.out.println(product.toString());
    }
}


输出如下:

 

 

有了这个注解之后,我们在转换bean时,就不需要在手工方式,为不能转换的属性进行赋值。即使以后返回数据反生变化,也能够快速的进行修改。不用修改大片代码。只需要修改注解name值就可以了。

 

通过@JSONType定制序列化

和@JSONField不同的是,@JSONType是配置在类上的,而@JSONField是配置在字段和方法上的

package com.jhaso.fastjson.test8;

import com.alibaba.fastjson.annotation.JSONType;

import java.io.Serializable;
import java.util.Date;

/**
 * @author 王帅兵
 * @ClassName User
 * @Description  使用ignores属性配置序列化的时候,不序列化id字段 
 * @create 2017-07-14 14:16
 * @version: V1.0.0
 **/
@JSONType(ignores = {"id"})
public class User implements Serializable {

    private Integer age;

    private Date date;

    private Integer id;

    private String name;

    private String sex;

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", date=" + date +
                ", id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}

 

package com.jhaso.fastjson.test8;

import com.alibaba.fastjson.annotation.JSONType;

import java.io.Serializable;
import java.util.Date;

/**
 * @author 王帅兵
 * @ClassName User2
 * @Description: 测试用的实体类
 *               使用@JSONType的includes只序列化name,sex字段
 * @create 2017-07-14 14:18
 * @version: V1.0.0
 **/
@JSONType(includes={"name", "sex"})
public class User2 implements Serializable {

    private Integer age;

    private Date date;

    private Integer id;

    private String name;

    private String sex;

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", date=" + date +
                ", id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}

测试代码:

package com.jhaso.fastjson.test8;

import com.alibaba.fastjson.JSON;

import java.util.Date;

/**
 * @author 王帅兵
 * @create 2017-07-14 12:54
 * @version: V1.0.0
 **/

public class UserTest {
    public static void main(String[] args) {
        User u = new User();
        u.setAge(10);
        u.setDate(new Date());
        u.setId(1);
        u.setName("李四");
        u.setSex("男");
        System.out.println("@JSONType ignores --> " + JSON.toJSONString(u));

        User2 u1 = new User2();
        u1.setAge(10);
        u1.setDate(new Date());
        u1.setId(1);
        u1.setName("李四");
        u1.setSex("男");
        System.out.println("@JSONType includes --> " + JSON.toJSONString(u1));
    }
}

 

测试结果:

 

通过SerializeFilter定制序列化

通过SerializeFilter可以使用扩展编程的方式实现定制序列化。fastjson提供了多种SerializeFilter:

· PropertyPreFilter 根据PropertyName判断是否序列化

· PropertyFilter 根据PropertyName和PropertyValue来判断是否序列化

· NameFilter 修改Key,如果需要修改Key,process返回值则可

· ValueFilter 修改Value

· BeforeFilter 序列化时在最前添加内容

· AfterFilter 序列化时在最后添加内容

以上的SerializeFilter在JSON.toJSONString中可以使用。

SerializeFilter filter = ..... // 可以是上面5个SerializeFilter的任意一种。
JSON.toJSONString(obj, filter);

 

1.PropertyPreFilter

 //大家可以根据set属性创建User对象

 User u = new User();
 u.setAge(10);
 u.setDate(new Date());
 u.setId(1);
 u.setName("李四");
 u.setSex("男");
 u.setFlag(true);
 /*
  *  定制序列化,只序列化一部分字段, 将需要序列化的字段名,配置到数组中
  *  如果什么都不配置,则序列化全部字段
  */
 // 只序列化name
 SimplePropertyPreFilter filter = new SimplePropertyPreFilter(User.class, new String[]{"name"});
 System.out.println("SimplePropertyPreFilter --> " + JSON.toJSONString(u, filter));

 

2.PropertyFilter

需求:A)根据age去判断如果年纪超过60的对象将不序列化。Eg:CompxPropertyPreByAgeFiler

     B)如果age>60, 只把age字段给过滤了,并把flag设置为false。Eg:UserTest2的PropertyFilter。

CompxPropertyPreByAgeFiler.java

 package com.jhaso.fastjson.test9;

import com.alibaba.fastjson.serializer.PropertyFilter;

/**
 * @author 王帅兵
 * @ClassName CompxPropertyPreByAgeFiler
 * @Description 自定义注解根据age去判断如果年纪超过60的对象将不序列化。
 * @create 2017-07-14 19:54
 * @version: V1.0.0
 **/
public class CompxPropertyPreByAgeFiler implements PropertyFilter {

    //@Override
    public boolean apply(Object source, String name, Object value) {
        User user = (User) source;
        if(user.getAge()>60){
            return false;
        }

      /*
      如果age>60 ,只把Age字段给过滤了。
       if ("age".equalsIgnoreCase(name)) {
            if ((Integer) value > 60)
                return false;
        }*/
        return true;
    }
}

 

UserTest2.java

package com.jhaso.fastjson.test9;

import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.alibaba.fastjson.serializer.SerializeWriter;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author 王帅兵
 * @create 2017-07-14 12:54
 * @version: V1.0.0
 **/

public class UserTest2 {
    public static void main(String[] args) {

        User user1 = new User(10,new Date(), 1, "李四", "男",true);
        User user2 = new User(70,new Date(), 15, "老四", "男",true);
        User user3 = new User(23,new Date(), 16, "小红", "女",true);

        List<User> list = new ArrayList<User>();
        list.add(user1);
        list.add(user2);
        list.add(user3);

        // 根据key和value判断是否需要序列化
        // 应用场景,hibernate懒加载过滤不要的属性)
        //方案一使用实现 PropertyFilter 接口实现
        CompxPropertyPreByAgeFiler filter1 = new CompxPropertyPreByAgeFiler();
        //方案二使用匿名内部了
        PropertyFilter filter2 = new PropertyFilter() {
            /**
             *
             * @param source 属性所属的对象eg:User
             * @param name 属性名
             * @param value 属性值
             * @return
             */
            //@Override
            public boolean apply(Object source, String name, Object value) {
                User user = (User) source;
                /*
                根据age去判断如果年纪超过60的对象将不序列化。
                if(user.getAge()>60){
                    return false;
                }*/


              //如果age>60 ,只把Age字段给过滤了。并把flag设置为false
               if ("age".equalsIgnoreCase(name)) {
                    if ((Integer) value > 60) {
                        user.setFlag(false);
                        return false;
                    }
               }
                        return true;
               }
        };
        //执行序列化
        SerializeWriter sw = new SerializeWriter();
        JSONSerializer serializer = new JSONSerializer(sw);
        serializer.getPropertyFilters().add(filter2);
        serializer.write(list);
        System.out.println(sw.toString());
    }
}

运行结果:

3.NameFilter

如果User的id为小写返回大写

package com.jhaso.fastjson.test10;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.NameFilter;
import com.jhaso.fastjson.test9.User;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author 王帅兵
 * @create 2017-07-14 12:54
 * @version: V1.0.0
 **/

public class UserTest {
    public static void main(String[] args) {

        User user1 = new User(10,new Date(), 1, "李四", "男",true);
        User user2 = new User(70,new Date(), 15, "老四", "男",true);
        User user3 = new User(23,new Date(), 16, "小红", "女",true);

        List<User> list = new ArrayList<User>();
        list.add(user1);
        list.add(user2);
        list.add(user3);

        // 序列化时修改key
        NameFilter nameFilter = new NameFilter() {
            /**
             * 如果需要修改Key,process返回值则可
             * 返回需要修改后的key值,如果不修改,则返回name
             * 切记不能返回null,否则会报错
             * @param object 属性所属的对象eg:User
             * @param name 属性名
             * @param value 属性值
             * @return
             */
            @Override
            public String process(Object object, String name, Object value) {
           /*     System.out.println("object ->" + object);
                System.out.println("name ->" + name);
                System.out.println("value ->" + value);*/
                if(name.equals("id")){
                    return "ID";
                }
                return name;
            }
        };
        System.out.println("NameFilter : " + JSON.toJSONString(list, nameFilter));

    }
}

 

4.ValueFilter

package com.jhaso.fastjson.test11;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.ValueFilter;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author 王帅兵
 * @create 2017-07-14 12:54
 * @version: V1.0.0
 **/

public class UserTest {
    public static void main(String[] args) {

        User user1 = new User(10,new Date(), 1, "李四", "男",true);
        User user2 = new User(70,new Date(), 15, "老四", "男",true);
        User user3 = new User(23,new Date(), 16, "小红", "女",true);

        List<User> list = new ArrayList<User>();
        list.add(user1);
        list.add(user2);
        list.add(user3);

        // 序列化时修改value
        ValueFilter valueFilter = new ValueFilter() {

            @Override
            public Object process(Object object, String name, Object value) {
                if(name.equals("name")){
                    return "张三";
                }
                return "";
            }
        };
        System.out.println("ValueFilter : " + JSON.toJSONString(list, valueFilter));
    }
}

 

 

5.BeforeFilter

package com.jhaso.fastjson.test12;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.BeforeFilter;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author 王帅兵
 * @create 2017-07-14 12:54
 * @version: V1.0.0
 **/

public class UserTest {
    public static void main(String[] args) {

        User user1 = new User(10,new Date(), 1, "李四", "男",true);
        User user2 = new User(70,new Date(), 15, "老四", "男",true);
        User user3 = new User(23,new Date(), 16, "小红", "女",true);

        List<User> list = new ArrayList<User>();
        list.add(user1);
        list.add(user2);
        list.add(user3);

        // 序列化时在最前添加内容
        BeforeFilter beforeFilter = new BeforeFilter() {

            @Override
            public void writeBefore(Object object) {
                writeKeyValue("hobby", "code java");
                writeKeyValue("phone", "18310008888");
            }
        };
        System.out.println("BeforeFilter : " + JSON.toJSONString(list, beforeFilter));
    }
}

运行结果:

6.AfterFilter

// 序列化之时在最后添加内容
AfterFilter afterFilter = new AfterFilter() {

    @Override
    public void writeAfter(Object object) {
        writeKeyValue("end", "after");
    }
};
System.out.println("AfterFilter : " + JSON.toJSONString(list, afterFilter));

运行结果:

 

FastJson定制化反序列化

1.使用@JSONField配置反序列化

2.使用ExtraProcessor和ExtraTypeProvider处理多余字段

我们首先来看一下第2步,使用ExtraProcessor和ExtraTypeProvider处理多余字段

package com.jhaso.fastjson.test13;

import com.alibaba.fastjson.parser.deserializer.ExtraProcessor;
import com.alibaba.fastjson.parser.deserializer.ExtraTypeProvider;

import java.lang.reflect.Type;

/**
 * @author 王帅兵
 * @ClassName MyProcesser
 * @Description
 * @create 2017-07-14 21:05
 * @version: V1.0.0
 **/
public class MyProcesser implements ExtraProcessor, ExtraTypeProvider {

    /**
     * 处理多余字段的类型
     */
    @Override
    public Type getExtraType(Object object, String key) {
        System.out.println("object -> " + object);
        System.out.println("type --> " + key);
        if (key.equals("class")) {
            return int.class;
        }
        return null;
    }

    /**
     * 处理多余字段时的回调
     */
    @Override
    public void processExtra(Object object, String key, Object value) {
        System.out.println(key + " : " + value);
    }

}

 

测试代码:

User user1 = new User(10,new Date(), 1, "李四");
User user2 = new User(70,new Date(), 15);
User user3 = new User(23,new Date(), 16, "小红");

List<User> list = new ArrayList<User>();
list.add(user1);
list.add(user2);
list.add(user3);

System.out.println("list -> " + list.toString());

// 处理多余字段
List<User> newList = JSON.parseObject(JSON.toJSONString(list),  List.class , new MyProcesser());
System.out.println("处理多余字段:" + newList.toString());

 

fastJson 序列化与反序列化时日期处理

fastjson提供了强大的日期处理和识别功能;在序列化的时候,支持多种方式,序列化为指定格式;反序列化的时候,也能识别多种格式的日期。下面来看看具体的代码:

一、序列化

fastjson序列化的时候,支持多种方式设置序列化日期:

1.序列化日期为ISO-8601日期格式

2.序列化日期为指定格式

3.用默认的全局日期格式

4.使用@JSONField配置

Eg:

package com.jhaso.fastjson.test14;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

import java.util.Date;

/**
 * @author 王帅兵
 * @ClassName FormatDateTest
 * @Description
 * @create 2017-07-14 22:08
 * @version: V1.0.0
 **/
public class FormatDateTest {

    public static void main(String[] args) {
        User user = new User();
        user.setId(1);
        user.setDate(new Date());

        // 日期不做处理
        System.out.println(JSON.toJSONString(user));
        // 序列化日期为ISO-8601日期格式
        System.out.println(JSON.toJSONString(user, SerializerFeature.UseISO8601DateFormat));
        // 序列化日期为指定格式
        System.out.println(JSON.toJSONStringWithDateFormat(user, "yyyy-MM-dd HH:mm:ss.SSS"));
        // 修改全局的全局日期格式
        JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd";
        // 使用默认的全局日期格式
        System.out.println(JSON.toJSONString(user, SerializerFeature.WriteDateUseDateFormat));
        // 使用@JSONField配置
        User user1 = new User();
        user1.setId(2);
        user1.setDate(new Date());
        System.out.println(JSON.toJSONString(user1));
        System.out.println();

    }
}

二、反序列化

反序列化能够自动识别如下日期格式:

· ISO-8601日期格式

· yyyy-MM-dd

· yyyy-MM-dd HH:mm:ss

· yyyy-MM-dd HH:mm:ss.SSS

· 毫秒数字

· 毫秒数字字符串

· .NET JSON日期格式

· new Date(1982932381111)

fastJson 循环引用

在fastjson中,默认是开启循环引用检测的功能的。什么叫循环引用呢?下面我们通过代码来讲解一下

V v = new V();

C c = new C();
c.v = v;

Z z = new Z();
z.v = v;

B b = new B();
b.c = c;
b.z = z;

 

从上面的代码我们可以看出,我们把V对象作为一个最基本的类,其中对象C中引用了v( c.v = v ),对象Z中也引用了V( z.v = v )。然后我们来看一下,当我们把B对象序列化后的输出结果是什么呢?

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

输出结果:{"c":{"v":{"name":"v"}},"z":{"v":{"$ref":"$.c.v"}}}  

我们可以看到输出的结果中z中引用的V对象输出为了 {"$ref":"$.c.v"},那么输出结果代表什么意思呢?我们来看看官网给的说明:

语法

描述

{"$ref":"$"}

引用根对象

{"$ref":"@"}

引用自己

{"$ref":".."}

引用父对象

{"$ref":"../.."}

引用父对象的父对象

{"$ref":"$.members[0].reportTo"}

基于路径的引用

 

通过上面我们可以知道,{"$ref":"$.c.v"}就是告诉我们引用的是C对象中的v;

有时候我们不需要循环引用,这个时候我们可以关闭fastjson的循环引用:

1.局部关闭

JSON.toJSONString(obj,SerializerFeature.DisableCircularReferenceDetect);

2.全局关闭

JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();


并且关闭循环引用,会提升fastjson的性能。

LabelFilter的使用

fastjson的LabelFilter提供了不同场景定制序列化的功能,也就是说我们可以根据我们的需要在不同的场景序列化不同字段的功能。

下面来看看简单的代码:

package com.jhaso.fastjson.test14;

import com.alibaba.fastjson.annotation.JSONField;

/**
 * @author 王帅兵
 * @ClassName V
 * @Description
 * @create 2017-07-14 22:30
 * @version: V1.0.0
 **/
public class V {
    private int id;
    private String name;
    private String info;
    private String password;

    @JSONField(label = "normal")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }

    @JSONField(label = "normal")
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getInfo() {
        return info;
    }
    public void setInfo(String info) {
        this.info = info;
    }

    @JSONField(label = "secret")
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    @Override
    public String toString() {
        return "V [id=" + id + ", name=" + name + ", info=" + info
                + ", password=" + password + "]";
    }

}

 

在字段的getXX()方法上配置label属性

public static void main(String[] args) {
    V v = new V();
    v.setId(123);
    v.setInfo("info");
    v.setName("name");
    v.setPassword("password");

    // 序列化label = "normal"的字段
    System.out.println(JSON.toJSONString(v, Labels.includes("normal")));
    // 序列化label != "normal"的字段
    System.out.println(JSON.toJSONString(v, Labels.excludes("normal")));
}

 

下面来看看序列化后的结果:

 

JSONPath使用

JSONPath是fastjson在1.2.0之后支持的。JSONPath是一个很强大的功能。关于JSONPath的介绍请查看官方文档 JSONPath

 官方文档上给出了详细的说明以及使用。但是官方文档没有具体的说明对于JSONPath在JSON数据中的使用,下面我来讲解一下JSONPath在JSON中的使用。

关于JSONPath在JSON中的使用,在1.2.3的时候,官方 support.odps 下面是有封装的;但是我下载的是1.2.7版本的,在1.2.7版本中在support包下面没有odps包,所以我下面的讲解是基于1.2.7版本的。

其实要在JSON数据中使用JSONPath很简单,只需要简单的一行代码:

 String jsonStr = "{ \"store\": {\"book\": [{ \"category\": \"reference\","+
                "\"author\": \"Nigel Rees\",\"title\": \"Sayings of the Century\","+
                "\"price\": 8.95},{ \"category\": \"fiction\",\"author\": \"Evelyn Waugh\","+
                "\"title\": \"Sword of Honour\",\"price\": 12.99,\"isbn\": \"0-553-21311-3\""+
                "}],\"bicycle\": {\"color\": \"red\",\"price\": 19.95}}}";
//Object jsonObject = JSON.parse(jsonStr); // 先解析JSON数据
        JSONObject jsonObject = JSON.parseObject(jsonStr);
    }

如果是json格式的字符串,则先解析为JSONObject,然后就能直接使用JSONPath了。

System.out.println("\n Book数目:" + JSONPath.eval(jsonObject, "$.store.book.size()"));
System.out.println("第一本书title:" + JSONPath.eval(jsonObject, "$.store.book[0].title"));
System.out.println("price大于10元的book:" + JSONPath.eval(jsonObject, "$.store.book[price > 10]"));
System.out.println("price大于10元的title:" + JSONPath.eval(jsonObject, "$.store.book[price > 10][0].title"));
System.out.println("category(类别)为fiction(小说)的book:" + JSONPath.eval(jsonObject, "$.store.book[category = 'fiction']"));
System.out.println("bicycle的所有属性值" + JSONPath.eval(jsonObject, "$.store.bicycle.*"));
System.out.println("bicycle的color和price属性值" + JSONPath.eval(jsonObject, "$.store.bicycle['color','price']"));

运行结果:

 

需要注意的是fastjson在序列化和反序列化的时候,默认是开启ASM的。关于ASM的介绍请查看官方文档 ASMDeserializerFactory 。

使用下面的代码就能关闭ASM:

SerializeConfig.getGlobalInstance().setAsmEnable(false); // 序列化的时候关闭ASM
ParserConfig.getGlobalInstance().setAsmEnable(false); // 反序列化的时候关闭ASM

 

 

转载于:https://my.oschina.net/u/2505908/blog/1383053

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值