fastjson使用经验

fastjson是阿里提供的json操作的开源项目。特点是api简单,效率高。

fastjson操作

fastjson的大部分操作都可以通过JSON对象静态调用。

将对象转json字符串

这样就可以将object转成json字符串

1
String jsonStr=JSON.toJSONString(object);

带有日期格式转化

1
2
public static String toJSONStringWithDateFormat(Object object, String dateFormat,
SerializerFeature... features) {

toJSONString方法还有很多重载方法,SerializeFilter可以用来过滤不需要转json字符串的字段。SerializerFeature配置序列化的特征。

1
public static String toJSONString(Object object, SerializeFilter[] filters, SerializerFeature... features)

SerializeConfig 序列化时的配置,可以添加特点类型自定义的序列化。

1
2
3
4
public static String toJSONString(Object object, //
SerializeConfig config, //
SerializeFilter filter, //
SerializerFeature... features)

将json字符串转为对象

将字符串转成对象可以通过JSON.parseObject方法,该方法有许多重载,可以定制反序列化。

如果没有加需要转成的类型的化,就会把对象转化为JSONObject

1
public static JSONObject parseObject(String text)

传入转化的class就会转成改对象

1
public static <T> T parseObject(String text, Class<T> clazz)

通过TypeReference构造转化类型

1
2
3
4
public static <T> T parseObject(String text, TypeReference<T> type, Feature... features)

//将json转为带泛型的map对象
Map<String,String> map=JSON.parseObject(msg,new TypeReference<Map<String,String>>(String.class,String.class){},null);

ParserConfig可以用于添加特定类型的自定义反序列化,ParseProcess用于处理json字符串中存在,对象中没有的额外字段

1
2
public static <T> T parseObject(String input, Type clazz, ParserConfig config, ParseProcess processor,
int featureValues, Feature... features)

反序列化数组可以通过parseArray,也可以用parseObject

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

注解

SerializerFeature

名称 含义
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 不推荐
PrettyFormat 结果是否格式化,默认为false
WriteClassName 序列化时写入类型信息,默认为false。反序列化是需用到
DisableCircularReferenceDetect 消除对同一对象循环引用的问题,默认为false
WriteSlashAsSpecial 对斜杠’/’进行转义
BrowserCompatible 将中文都会序列化为\uXXXX格式,字节数会多一些,但是能兼容IE 6,默认为false
WriteDateUseDateFormat 全局修改日期格式,默认为false。JSON.DEFFAULT_DATE_FORMAT = “yyyy-MM-dd”;JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat);
DisableCheckSpecialChar 一个对象的字符串属性中如果有特殊字符如双引号,将会在转成json时带有反斜杠转移符。如果不需要转义,可以使用这个属性。默认为false
NotWriteRootClassName 含义
BeanToArray 将对象转为array输出
WriteNonStringKeyAsString 含义
NotWriteDefaultValue 含义
BrowserSecure 含义
IgnoreNonFieldGetter 含义
WriteEnumUsingName 枚举类型通过名字转化

例子

自定义的枚举类型反序列化

fastjson默认提供的对枚举类型的反序列化的方式有两种

  1. 根据枚举的name
  2. 根据枚举的ordinal值

但是如果需要通过自定义的值反序列化成枚举,就需要自定义反序列化器。
如下面枚举,我想通过EnumValue接口返回的value值进行反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface EnumValue{
int value();
}
enum TestEnum implements EnumValue{
TEST1(1),
TEST2(2),
;
private int value;
TestEnum(int value){
this.value=value;
}
@Override
public int value() {
return value;
}
}

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
class Bean{
private int id;
private TestEnum testEnum;

public int getId() {
return id;
}

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

public TestEnum getTestEnum() {
return testEnum;
}

public void setTestEnum(TestEnum testEnum) {
this.testEnum = testEnum;
}

@Override
public String toString() {
return "Bean{" +
"id=" + id +
", testEnum=" + testEnum +
'}';
}
}

通过下面代码直接序列化,int类型的值会自动用ordinal,ordinal从0开始 ,TestEnum枚举有2个,ordinal值从0-1,这里会发生ArrayIndexOutOfBoundsException

1
2
3
String jsonStr = "{\"id\":1,\"testEnum\":2}";
Bean bean = JSON.parseObject(jsonStr, Bean.class);
System.out.println(bean);

解决方法:

自定义反序列化

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
33
34
35
36
37
38
39
40
41
42
43
class EnumValueDeserializer implements ObjectDeserializer {

@Override
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
final JSONLexer lexer = parser.lexer;
final int token = lexer.token();
Class cls = (Class) type;
Object[] enumConstants = cls.getEnumConstants();
if (EnumValue.class.isAssignableFrom(cls)) {
int value;
if (token == JSONToken.LITERAL_INT){
value =lexer.intValue();
}else{
value=Integer.parseInt(lexer.stringValue);
}
lexer.nextToken(JSONToken.COMMA);
for (Object enumConstant : enumConstants) {
if (((EnumValue) enumConstant).value() ==value ) {
return (T) enumConstant;
}
}
} else {
//没实现EnumValue接口的 默认的按名字或者按ordinal
if (token == JSONToken.LITERAL_INT) {
int intValue = lexer.intValue();
lexer.nextToken(JSONToken.COMMA);

if (intValue < 0 || intValue > enumConstants.length) {
throw new JSONException("parse enum " + cls.getName() + " error, value : " + intValue);
}
return (T) enumConstants[intValue];
} else if (token == JSONToken.LITERAL_STRING) {
return (T) Enum.valueOf(cls, lexer.stringVal());
}
}
return null;
}

@Override
public int getFastMatchToken() {
return JSONToken.LITERAL_INT;
}
}

注册反序列化器

注册有三种方法

通过ParserConfig注册

1
2
3
4
5
String jsonStr = "{\"id\":1,\"testEnum\":2}";
ParserConfig globalInstance = ParserConfig.getGlobalInstance();
globalInstance.putDeserializer((Type) TestEnum.class, new EnumValueDeserializer());
Bean bean = JSON.parseObject(jsonStr, Bean.class);
System.out.println(bean);

通过JSONType注解注册

1
2
@JSONType(deserializer = EnumValueDeserializer.class)
enum TestEnum implements EnumValue {

#### 复写ParserConfig,替换掉默认的枚举反序列化器,这种方式是全局的,对所有枚举起作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  class MyParserConfig extends ParserConfig{
@Override
public ObjectDeserializer getDeserializer(Class<?> clazz, Type type) {
ObjectDeserializer derializer;
if (clazz.isEnum()) {
Class<?> deserClass = null;
JSONType jsonType = clazz.getAnnotation(JSONType.class);
if (jsonType != null) {
deserClass = jsonType.deserializer();
try {
derializer = (ObjectDeserializer) deserClass.newInstance();
this.putDeserializer(type,derializer);
return derializer;
} catch (Throwable error) {
// skip
}
}
//这里替换了原来的反序列化器。
derializer = new EnumValueDeserializer();
return derializer;
}
return super.getDeserializer(clazz, type);
}

1
2
3
String jsonStr = "{\"id\":1,\"testEnum\":2}";
Bean bean = JSON.parseObject(jsonStr,(Type)Bean.class,new MyParserConfig());
System.out.println(bean);

反序列化不通过setter方法。

反序列化字段不通过setter方法时,可以使用Feature.SupportNonPublicField。

1
2
Test obj = JSON.parseObject(msg, new TypeReference<Test>() {
}.getType(), Feature.SupportNonPublicField);

反序列化带有get方法的list字段

但是对于对象中带有get方法的list字段,fastjson的处理:
通过get方法获取list或map,如果是null不会处理。

以下带来。com.alibaba.fastjson.parser.deserializer.FieldDeserializer类 setValue方法片段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
else if (Map.class.isAssignableFrom(method.getReturnType())) {
Map map = (Map) method.invoke(object);
if (map != null) {
if (map == Collections.emptyMap()
|| map.getClass().getName().startsWith("java.util.Collections$Unmodifiable")) {
// skip
return;
}
map.putAll((Map) value);
}
} else {
Collection collection = (Collection) method.invoke(object);
if (collection != null && value != null) {
if (collection == Collections.emptySet()
|| collection == Collections.emptyList()
|| collection.getClass().getName().startsWith("java.util.Collections$Unmodifiable")) {
// skip
return;
}
collection.clear();
collection.addAll((Collection) value);
}
}

所以以下例子反序列化出来的ids属性为null。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test {

private int id;
private List<Integer> ids;

public int getId() {
return id;
}

public List<Integer> getIds() {
return ids;
}
}

1
2
3
4
5
6
7
8
String msg="[" +
"{" +
"\"id\":1," +
"\"ids\":[1,2]" +
"}" +
"]";
Test obj = JSON.parseObject(msg, new TypeReference<Test>() {
}.getType(), Feature.SupportNonPublicField);

解决方法

1
2
3
 //ParserConfig 构造函数中将fieldbased设置为true,开启只基于字段进行反序列化。
JSON.parseObject(msg,ew TypeReference<Test>() {
}.getType(),new ParserConfig(true));

设置了fieldBased,反序列化处理来ids就有值了