Fastjson是一个Java语言编写的高性能功能完善的JSON库。它采用一种“假定有序快速匹配”的算法,把JSON Parse的性能提升到极致,是目前Java语言中最快的JSON库。Fastjson接口简单易用,已经被广泛使用在缓存序列化、协议交互、Web输出、Android客户端等多种应用场景。
主要API
fastjson入口类是com.alibaba.fastjson.JSON,主要的API是JSON.toJSONString,和parseObject。
序列化
1
String jsonString = JSON.toJSONString(obj);
反序列化
1
Vo vo = JSON.parseObject("...",Vo.class);
泛型反序列化
1
List<Vo> list = JSON.parserObject("...",new TypeReference<List<Vo>>(){});
定制序列化
通过@JSONField定制序列化
JSONField介绍
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package 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;
}配置方式
可以配置在getter/setter方法或者字段上。例如:1
2
3
4
5
6
7
8public class A {
private int id;
@JSONField(name="ID")
public int getId() {return id;}
@JSONField(name="ID")
public void setId(int value) {this.id = id;}
}
1 | public class A { |
使用format配置日期格式化
1
2
3
4
5public class A {
// 配置date序列化和反序列使用yyyyMMdd日期格式
@JSONField(format="yyyyMMdd")
public Date date;
}序列化、反序列化时排除指定字段
1
2
3
4
5
6
7
8
9public class A {
@JSONField(serialize=false)
public Date date;
}
public class A {
@JSONField(deserialize=false)
public Date date;
}使用ordinal指定字段的顺序。缺省根据fieldName的字母序进行序列化
1
2
3
4
5
6
7
8
9
10public static class VO {
@JSONField(ordinal = 3)
private int f0;
@JSONField(ordinal = 2)
private int f1;
@JSONField(ordinal = 1)
private int f2;
}
使用@JSONType配置
和JSONField类似,但JSONType配置在类上,而不是field或者getter/setter方法上。
通过SerializeFilter定制序列化
通过SerializeFilter可以使用扩展编程的方式实现定制序列化。fastjson提供了多种SerializeFilter:
- PropertyPreFilter 根据PropertyName判断是否序列化
- PropertyFilter 根据PropertyName和PropertyValue来判断是否序列化
- NameFilter 修改Key,如果需要修改Key,process返回值则可
- ValueFilter 修改Value
- BeforeFilter 序列化时在最前添加内容
- AfterFilter 序列化时在最后添加内容
以上的SerializeFilter在JSON.toJSONString中可以使用。1
2SerializeFilter filter = ...; // 可以是上面5个SerializeFilter的任意一种。
JSON.toJSONString(obj, filter);
PropertyFilter 根据PropertyName和PropertyValue来判断是否序列化
1 | public interface PropertyFilter extends SerializeFilter { |
可以通过扩展实现根据object或者属性名称或者属性值进行判断是否需要序列化。例如:1
2
3
4
5
6
7
8
9
10
11
12PropertyFilter 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
PropertyPreFilter 根据PropertyName判断是否序列化
和PropertyFilter不同只根据object和name进行判断,在调用getter之前,这样避免了getter调用可能存在的异常。1
2
3public interface PropertyPreFilter extends SerializeFilter {
boolean apply(JSONSerializer serializer, Object object, String name);
}
- SimplePropertyPreFilter接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class SimplePropertyPreFilter implements PropertyPreFilter {
public SimplePropertyPreFilter(String... properties){
this(null, properties);
}
public SimplePropertyPreFilter(Class<?> clazz, String... properties){
// ... ...
}
public Class<?> getClazz() {
return clazz;
}
public Set<String> getIncludes();
public Set<String> getExcludes();
/** * @since 1.2.9 */public int getMaxLevel();
/** * @since 1.2.9 */public void setMaxLevel(int maxLevel)
//...
}
你可以配置includes、excludes。当class不为null时,针对特定类型;当class为null时,针对所有类型。
当includes的size > 0时,属性必须在includes中才会被序列化,excludes优先于includes。
- 使用方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26VO vo = new VO();
vo.setId(123);
vo.setName("flym");
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(VO.class, "name");
Assert.assertEquals("{\"name\":\"flym\"}", JSON.toJSONString(vo, filter));
public static class VO {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
NameFilter 序列化时修改Key
如果需要修改Key,process返回值则可1
2
3public interface NameFilter extends SerializeFilter {
String process(Object object, String propertyName, Object propertyValue);
}
fastjson内置一个PascalNameFilter,用于输出将首字符大写的Pascal风格。 例如:1
2
3
4import com.alibaba.fastjson.serializer.PascalNameFilter;
Object obj = ...;
String jsonStr = JSON.toJSONString(obj, new PascalNameFilter());
ValueFilter 序列化是修改Value
1 | public interface ValueFilter extends SerializeFilter { |
BeforeFilter 序列化时在最前添加内容
1 | public abstract class BeforeFilter implements SerializeFilter { |
AfterFilter 序列化时在最前添加内容
1 | public abstract class AfterFilter implements SerializeFilter { |
类级别配置Filter
对于框架来说,如果在toJSONString的时候,传入SerializeFilter,会导致对所有的类型做过滤,性能会受到一定影响。在1.2.10版本之后,fastjson提供类级别的SerializeFilter支持。1
2
3
4
5package com.alibaba.fastjson.serializer;
public class SerializeConfig {
public void addFilter(Class<?> clazz, SerializeFilter filter);
}
1 | public class ClassNameFilterTest extends TestCase { |
BeanToArray
在fastjson中,支持一种叫做BeanToArray的映射模式。普通模式下,JavaBean映射成json object,BeanToArray模式映射为json array。
- Sample 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Mode {
public int id;
public int name;
}
Model model = new Model();
model.id = 1001;
model.name = "gaotie";
// {"id":1001,"name":"gaotie"}
String text_normal = JSON.toJSONString(model);
// [1001,"gaotie"]
String text_beanToArray = JSON.toJSONString(model, SerializerFeature.BeanToArray);
// support beanToArray & normal mode
JSON.parseObject(text_beanToArray, Mode.class, Feature.SupportArrayToBean);
上面的例子中,BeanToArray模式下,少了Key的输出,节省了空间,json字符串较小,性能也会更好。
- Sample 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class Company {
public int code;
public List<Department> departments = new ArrayList<Department>();
}
@JSONType(serialzeFeatures=SerializerFeature.BeanToArray, parseFeatures=Feature.SupportArrayToBean))
class Department {
public int id;
public Stirng name;
public Department() {}
public Department(int id, String name) {this.id = id; this.name = name;}
}
Company company = new Company();
company.code = 100;
company.departments.add(new Department(1001, "Sales"));
company.departments.add(new Department(1002, "Financial"));
// {"code":10,"departments":[[1001,"Sales"],[1002,"Financial"]]}
String text = JSON.toJSONString(commpany);
在这个例子中,如果Company的属性departments元素很多,局部采用BeanToArray就可以获得很好的性能,而整体又能够获得较好的可读性。
性能
使用BeanToArray模式,可以获得媲美protobuf的性能。通过ParseProcess定制反序列化
ParseProcess是编程扩展定制反序列化的接口。fastjson支持如下ParseProcess:
ExtraProcessor 用于处理多余的字段
- ExtraTypeProvider 用于处理多余字段时提供类型信息
使用ExtraProcessor 处理多余字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public 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"));
使用ExtraTypeProvider 为多余的字段提供类型
1 | public static class VO { |
自定义序列化
fastjson提供了自定义序列化的接口,当你通过配置无法满足自定义序列化需求时,你可以使用ObjectSerializer接口。
API
ObjectSerializer接口
1
2
3
4
5
6
7
8
9package com.alibaba.fastjson.serializer;
public interface ObjectSerializer {
void write(JSONSerializer serializer,
Object object,
Object fieldName,
Type fieldType,
int features) throws IOException;
}注册API
1
2
3
4
5package com.alibaba.fastjson.serializer;
public class SerializeConfig {
public boolean put(Type key, ObjectSerializer value);
}
Sample
实现ObjectSerializer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public class CharacterSerializer implements ObjectSerializer {
public void write(JSONSerializer serializer,
Object object,
Object fieldName,
Type fieldType,
int features) throws IOException {
SerializeWriter out = serializer.out;
Character value = (Character) object;
if (value == null) {
out.writeString("");
return;
}
char c = value.charValue();
if (c == 0) {
out.writeString("\u0000");
} else {
out.writeString(value.toString());
}
}
}注册
1
SerializeConfig.getGlobalInstance().put(Character.class, new CharacterSerializer());
自定义反序列化
fastjson支持注册ObjectDeserializer实现自定义反序列化。要自定义序列化,首先需要实现一个ObjectDeserializer,然后注册到ParserConfig中。
API
ObjectDeserializer 定义
1
2
3
4
5
6
7package com.alibaba.fastjson.parser.deserializer;
public interface ObjectDeserializer {
<T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName);
int getFastMatchToken();
}注册自定义ObjectDeserializer API
1
2
3
4
5package com.alibaba.fastjson.parser;
public class ParserConfig {
public void putDeserializer(Type type, ObjectDeserializer deserializer);
}
Sample
Model定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public static enum OrderActionEnum {
FAIL(1), SUCC(0);
private int code;
OrderActionEnum(int code){
this.code = code;
}
}
public static class Msg {
public OrderActionEnum actionEnum;
public String body;
}自定义实现ObjectDeserializer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public static class OrderActionEnumDeser implements ObjectDeserializer {
@SuppressWarnings("unchecked")
@Overridepublic <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
Integer intValue = parser.parseObject(int.class);
if (intValue == 1) {
return (T) OrderActionEnum.FAIL;
} else if (intValue == 0) {
return (T) OrderActionEnum.SUCC;
}
throw new IllegalStateException();
}
@Overridepublic int getFastMatchToken() {
return JSONToken.LITERAL_INT;
}
}注册并使用ObjectDeserializer
1
2
3
4
5
6
7
8
9
10
11
12ParserConfig.getGlobalInstance().putDeserializer(OrderActionEnum.class, new OrderActionEnumDeser());
{
Msg msg = JSON.parseObject("{\"actionEnum\":1,\"body\":\"A\"}", Msg.class);
Assert.assertEquals(msg.body, "A");
Assert.assertEquals(msg.actionEnum, OrderActionEnum.FAIL);
}
{
Msg msg = JSON.parseObject("{\"actionEnum\":0,\"body\":\"B\"}", Msg.class);
Assert.assertEquals(msg.body, "B");
Assert.assertEquals(msg.actionEnum, OrderActionEnum.SUCC);
}
处理超大对象和超大JSON文本
当需要处理超大JSON文本时,需要Stream API,在fastjson-1.1.32版本中开始提供Stream API。
序列化
超大JSON数组序列化
如果你的JSON格式是一个巨大的JSON数组,有很多元素,则先调用startArray,然后挨个写入对象,然后调用endArray。1
2
3
4
5
6
7JSONWriter writer = new JSONWriter(new FileWriter("/tmp/huge.json"));
writer.startArray();
for (int i = 0; i < 1000 * 1000; ++i) {
writer.writeValue(new VO());
}
writer.endArray();
writer.close();
超大JSON对象序列化
如果你的JSON格式是一个巨大的JSONObject,有很多Key/Value对,则先调用startObject,然后挨个写入Key和Value,然后调用endObject。1
2
3
4
5
6
7
8JSONWriter writer = new JSONWriter(new FileWriter("/tmp/huge.json"));
writer.startObject();
for (int i = 0; i < 1000 * 1000; ++i) {
writer.writeKey("x" + i);
writer.writeValue(new VO());
}
writer.endObject();
writer.close();
反序列化
1 | JSONReader reader = new JSONReader(new FileReader("/tmp/huge.json")); |
1 | JSONReader reader = new JSONReader(new FileReader("/tmp/huge.json")); |
LabelFilter
在Fastjson 1.2.7版本之后,fastjson提供LabelFilter功能,用于不同的场景定制序列化。
需要使用LabelFilter,首先需要用@JSONField配置label。如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33public static class VO {
private int id;
private String name;
private String password;
private String info;
@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;
}
@JSONField(label = "secret")
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
根据不同场景过滤,如下:1
2
3
4
5
6
7
8
9
10
11VO vo = new VO();
vo.setId(123);
vo.setName("wenshao");
vo.setPassword("ooxxx");
vo.setPassword("ooxxx");
String text1 = JSON.toJSONString(vo, Labels.includes("normal"));
Assert.assertEquals("{\"id\":123,\"name\":\"wenshao\"}", text1);
String text2 = JSON.toJSONString(vo, Labels.excludes("secret"));
Assert.assertEquals("{\"id\":123,\"info\":\"fofo\",\"name\":\"wenshao\"}", text2);
从流中反序列化
在1.2.11版本中,fastjson新增加了对InputStream的支持1
2
3
4
5
6
7package com.alibaba.fastjson;
public abstract class JSON {
public static <T> T parseObject(InputStream is, //Type type, //Feature... features) throws IOException;
public static <T> T parseObject(InputStream is, //Charset charset, //Type type, //Feature... features) throws IOException;
}
- Sample
1
2
3
4
5
6
7
8import com.alibaba.fastjson;
import java.nio.charset.Charset;
class Model {
public int value;
}
InputStream is = ...Model model = JSON.parseObject(is, Model.class);
Model model2 = JSON.parseObject(is, Charset.from("UTF-8"), Model.class);
将对象序列化到流中
在1.2.11版本中,JSON类新增对OutputStream/Writer直接支持。1
2
3
4
5
6
7
8
9package com.alibaba.fastjson;
public abstract class JSON {
public static final int writeJSONString(OutputStream os, // Object object, // SerializerFeature... features) throws IOException;
public static final int writeJSONString(OutputStream os, //Charset charset, // Object object, // SerializerFeature... features) throws IOException;
public static final int writeJSONString(Writer os, // Object object, // SerializerFeature... features) throws IOException;
}
- Sample
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import com.alibaba.fastjson;
import java.nio.charset.Charset;
class Model {
public int value;
}
Model model = new Model();
model.value = 1001;
OutputStream os = ...;
JSON.writeJSONString(os, model);
JSON.writeJSONString(os, Charset.from("GB18030"), model);
Writer writer = ...;
JSON.writeJSONString(os, model);
JSONPath
fastjson 1.2.0之后的版本支持JSONPath。这是一个很强大的功能,可以在java框架中当作对象查询语言(OQL)来使用。
API
1 | package com.alibaba.fastjson; |
建议缓存JSONPath对象,这样能够提高求值的性能。
支持语法
JSONPATH | 描述 |
---|---|
$ | 根对象,例如$.name |
[num] | 数组访问,其中num是数字,可以是负数。例如$[0].leader.departments[-1].name |
[num0,num1,num2…] | 数组多个元素访问,其中num是数字,可以是负数,返回数组中的多个元素。例如$[0,3,-2,5] |
[start:end] | 数组范围访问,其中start和end是开始小表和结束下标,可以是负数,返回数组中的多个元素。例如$[0:5] |
[start:end :step] | 数组范围访问,其中start和end是开始小表和结束下标,可以是负数;step是步长,返回数组中的多个元素。例如$[0:5:2] |
[?(key)] | 对象属性非空过滤,例如$.departs[?(name)] |
[key > 123] | 数值类型对象属性比较过滤,例如$.departs[id >= 123],比较操作符支持=,!=,>,>=,<,<= |
[key = ‘123’] | 字符串类型对象属性比较过滤,例如$.departs[name = ‘123’],比较操作符支持=,!=,>,>=,<,<= |
[key like ‘aa%’] | 字符串类型like过滤,例如$.departs[name like ‘sz*’],通配符只支持% ,支持not like |
[key rlike ‘regexpr’] | 字符串类型正则匹配过滤,例如departs[name like ‘aa(.)*’],正则语法为jdk的正则语法,支持not rlike |
[key in (‘v0’, ‘v1’)] | IN过滤, 支持字符串和数值类型 例如 \$.departs[name in (‘wenshao’,’Yako’)] \$.departs[id not in (101,102)] |
[key between 234 and 456] | BETWEEN过滤, 支持数值类型,支持not between 例如 \$.departs[id between 101 and 201],\$.departs[id not between 101 and 201] |
length() 或者 size() | 数组长度。例如$.values.size(),支持类型java.util.Map和java.util.Collection和数组 |
. | 属性访问,例如$.name |
* | 对象的所有属性,例如$.leader.* |
[‘key’] | 属性访问。例如$[‘name’] |
[‘key0’,’key1’] | 多个属性访问。例如$[‘id’,’name’] |
以下两种写法的语义是相同的:1
$.store.book[0].title
1 | $['store']['book'][0]['title'] |
语法示例
JSONPath | 语义 |
---|---|
$ | 根对象 |
$[-1] | 最后元素 |
$[:-2] | 第1个至倒数第2个 |
$[1:] | 第2个之后所有元素 |
$[1,2,3] | 集合中1,2,3个元素 |
API 示例
1 | public void test_entity() throws Exception { |
读取集合多个元素的某个属性
1
2
3
4
5
6
7List<Entity> entities = new ArrayList<Entity>();
entities.add(new Entity("wenshao"));
entities.add(new Entity("ljw2083"));
List<String> names = (List<String>)JSONPath.eval(entities, "$.name"); // 返回enties的所有名称
Assert.assertSame(entities.get(0).getName(), names.get(0));
Assert.assertSame(entities.get(1).getName(), names.get(1));返回集合中多个元素
1
2
3
4
5
6
7
8
9List<Entity> entities = new ArrayList<Entity>();
entities.add(new Entity("wenshao"));
entities.add(new Entity("ljw2083"));
entities.add(new Entity("Yako"));
List<Entity> result = (List<Entity>)JSONPath.eval(entities, "[1,2]"); // 返回下标为1和2的元素
Assert.assertEquals(2, result.size());
Assert.assertSame(entities.get(1), result.get(0));
Assert.assertSame(entities.get(2), result.get(1));按范围返回集合的子集
1
2
3
4
5
6
7
8
9
10List<Entity> entities = new ArrayList<Entity>();
entities.add(new Entity("wenshao"));
entities.add(new Entity("ljw2083"));
entities.add(new Entity("Yako"));
List<Entity> result = (List<Entity>)JSONPath.eval(entities, "[0:2]"); // 返回下标从0到2的元素
Assert.assertEquals(3, result.size());
Assert.assertSame(entities.get(0), result.get(0));
Assert.assertSame(entities.get(1), result.get(1));
Assert.assertSame(entities.get(2), result.get(1));通过条件过滤,返回集合的子集
1
2
3
4
5
6
7
8
9List<Entity> entities = new ArrayList<Entity>();
entities.add(new Entity(1001, "ljw2083"));
entities.add(new Entity(1002, "wenshao"));
entities.add(new Entity(1003, "yakolee"));
entities.add(new Entity(1004, null));
List<Object> result = (List<Object>) JSONPath.eval(entities, "[id in (1001)]");
Assert.assertEquals(1, result.size());
Assert.assertSame(entities.get(0), result.get(0));根据属性值过滤条件判断是否返回对象,修改对象,数组属性添加元素
1
2
3
4
5
6
7
8
9Entity entity = new Entity(1001, "ljw2083");
Assert.assertSame(entity , JSONPath.eval(entity, "[id = 1001]"));
Assert.assertNull(JSONPath.eval(entity, "[id = 1002]"));
JSONPath.set(entity, "id", 123456); //将id字段修改为123456
Assert.assertEquals(123456, entity.getId().intValue());
JSONPath.set(entity, "value", new int[0]); //将value字段赋值为长度为0的数组
JSONPath.arrayAdd(entity, "value", 1, 2, 3); //将value字段的数组添加元素1,2,3