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