Fastjson介绍 html
https://github.com/alibaba/fastjson前端
Fastjson是一个Java语言编写的JSON处理器,由阿里巴巴公司开发。
一、遵循http://json.org标准,为其官方网站收录的参考实现之一。
二、功能qiang打,支持JDK的各类类型,包括基本的JavaBean、Collection、Map、Date、Enum、泛型。
三、无依赖,不须要例外额外的jar,可以直接跑在JDK上。
四、开源,使用Apache License 2.0协议开源。http://code.alibabatech.com/wiki/display/FastJSON/Home
五、具备超高的性能,java世界里没有其余的json库可以和fastjson可相比了。java
1.添加依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.60</version> </dependency>
Fastjson的最主要的使用入口是com.alibaba.fastjson.JSONgit
import com.alibaba.fastjson.JSON; public static final Object parse(String text); // 把JSON文本parse为JSONObject或者JSONArray public static final JSONObject parseObject(String text); // 把JSON文本parse成JSONObject public static final <T> T parseObject(String text, Class<T> clazz); // 把JSON文本parse为JavaBean public static final JSONArray parseArray(String text); // 把JSON文本parse成JSONArray public static final <T> List<T> parseArray(String text, Class<T> clazz); //把JSON文本parse成JavaBean集合 public static final String toJSONString(Object object); // 将JavaBean序列化为JSON文本 public static final String toJSONString(Object object, boolean prettyFormat); // 将JavaBean序列化为带格式的JSON文本 public static final Object toJSON(Object javaObject); 将JavaBean转换为JSONObject或者JSONArray。
使用github.com/eishay/jvm-serializers/提供的程序作测试,性能数据以下:仅供参考github
2. 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 含义
3. 演示示例
3.1 编写实体类User,Word来模拟各类数据类型
User实体以下(缺省setter,getter方法):web
public class User { private int id; private String name; private String add; private String old; }
Word实体以下(缺省setter,getter方法):算法
public class Word { private String d; private String e; private String f; private String a; private int b; private boolean c; private Date date; private Map<String , Object> map; private List<User> list; }
3.2 编写Main进行测试
初始化数据以下:spring
public class Main { private static Word word; private static void init() { word = new Word(); word.setA("a"); word.setB(2); word.setC(true); word.setD("d"); word.setE(""); word.setF(null); word.setDate(new Date()); List<User> list = new ArrayList<User>(); User user1 = new User(); user1.setId(1); user1.setOld("11"); user1.setName("用户1"); user1.setAdd("北京"); User user2 = new User(); user2.setId(2); user2.setOld("22"); user2.setName("用户2"); user2.setAdd("上海"); User user3 = new User(); user3.setId(3); user3.setOld("33"); user3.setName("用户3"); user3.setAdd("广州"); list.add(user3); list.add(user2); list.add(null); list.add(user1); word.setList(list); Map<String, Object> map = new HashMap<String, Object>(); map.put("mapa", "mapa"); map.put("mapo", "mapo"); map.put("mapz", "mapz"); map.put("user1", user1); map.put("user3", user3); map.put("user4", null); map.put("list", list); word.setMap(map); } public static void main(String[] args) { init(); useSingleQuotes(); // writeMapNullValue(); // useISO8601DateFormat(); // writeNullListAsEmpty(); // writeNullStringAsEmpty(); // sortField(); // prettyFormat(); // writeDateUseDateFormat(); // beanToArray(); //showJsonBySelf(); } }
3.2.1 UseSingleQuotes:使用单引号而不是双引号,默认为falsejson
/** * 1: UseSingleQuotes:使用单引号而不是双引号,默认为false */ private static void useSingleQuotes() { System.out.println(JSONObject.toJSONString(word)); System.out.println("设置useSingleQuotes后:"); System.out.println(JSONObject.toJSONString(word, SerializerFeature.UseSingleQuotes)); }
3.2.2 WriteMapNullValue:是否输出值为null的字段,默认为falsespringboot
/** * 2:WriteMapNullValue:是否输出值为null的字段,默认为false * */ private static void writeMapNullValue() { System.out.println(JSONObject.toJSONString(word)); System.out.println("设置WriteMapNullValue后:"); System.out.println(JSONObject.toJSONString(word, SerializerFeature.WriteMapNullValue)); }
3.2.3 UseISO8601DateFormat:Date使用ISO8601格式输出,默认为false
/** * 3:UseISO8601DateFormat:Date使用ISO8601格式输出,默认为false * */ private static void useISO8601DateFormat() { System.out.println(JSONObject.toJSONString(word)); System.out.println("设置UseISO8601DateFormat后:"); System.out.println(JSONObject.toJSONString(word, SerializerFeature.UseISO8601DateFormat)); }
3.2.4 WriteNullListAsEmpty:List字段若是为null,输出为[],而非null,须要配合WriteMapNullValue使用,现将null输出
/** * 4: * WriteNullListAsEmpty:List字段若是为null,输出为[],而非null * 须要配合WriteMapNullValue使用,现将null输出 */ private static void writeNullListAsEmpty() { word.setList(null); System.out.println(JSONObject.toJSONString(word)); System.out.println("设置WriteNullListAsEmpty后:"); System.out.println(JSONObject.toJSONString(word, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty)); }
3.2.5 WriteNullStringAsEmpty:字符类型字段若是为null,输出为””,而非null,须要配合WriteMapNullValue使用,现将null输出
/** * 5: * WriteNullStringAsEmpty:字符类型字段若是为null,输出为"",而非null * 须要配合WriteMapNullValue使用,现将null输出 */ private static void writeNullStringAsEmpty() { word.setE(null); System.out.println(JSONObject.toJSONString(word)); System.out.println("设置WriteMapNullValue后:"); System.out.println(JSONObject.toJSONString(word, SerializerFeature.WriteMapNullValue)); System.out.println("设置WriteMapNullValue、WriteNullStringAsEmpty后:"); System.out.println(JSONObject.toJSONString(word, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty)); }
3.2.6 SortField:按字段名称排序后输出。默认为false
/** * SortField:按字段名称排序后输出。默认为false * 这里使用的是fastjson:为了更好使用sort field martch优化算法提高parser的性能,fastjson序列化的时候, * 缺省把SerializerFeature.SortField特性打开了。 * 反序列化的时候也缺省把SortFeidFastMatch的选项打开了。 * 这样,若是你用fastjson序列化的文本,输出的结果是按照fieldName排序输出的,parser时也能利用这个顺序进行优化读取。 * 这种状况下,parser可以得到很是好的性能。 */ private static void sortField() { System.out.println(JSON.toJSONString(word)); System.out.println(JSON.toJSONString(word, SerializerFeature.SortField)); }
3.2.7 PrettyFormat
/** * 7: * PrettyFormat */ private static void prettyFormat() { word.setMap(null); word.setList(null); System.out.println(JSON.toJSONString(word)); System.out.println(JSON.toJSONString(word, SerializerFeature.PrettyFormat)); }
3.2.8 WriteDateUseDateFormat:全局修改日期格式,默认为false。
/** * 8: * WriteDateUseDateFormat:全局修改日期格式,默认为false。 */ private static void writeDateUseDateFormat() { word.setMap(null); word.setList(null); System.out.println(JSON.toJSONString(word)); JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd"; System.out.println(JSON.toJSONString(word, SerializerFeature.WriteDateUseDateFormat)); }
3.2.9 将对象转为array输出
/** * 8: * 将对象转为array输出 */ private static void beanToArray() { word.setMap(null); word.setList(null); System.out.println(JSON.toJSONString(word)); System.out.println(JSON.toJSONString(word, SerializerFeature.BeanToArray)); }
3.2.9 自定义
/** * 9:自定义 * 格式化输出 * 显示值为null的字段 * 将为null的字段值显示为"" * DisableCircularReferenceDetect:消除循环引用 */ private static void showJsonBySelf() { System.out.println(JSON.toJSONString(word)); System.out.println(JSON.toJSONString(word, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteNullListAsEmpty)); }
3.2.10 特别注意:循环引用检测
fastjson把对象转化成json避免$ref
DisableCircularReferenceDetect来禁止循环引用检测:
JSON.toJSONString(..., SerializerFeature.DisableCircularReferenceDetect)
当进行toJSONString的时候,默认若是重用对象的话,会使用引用的方式进行引用对象。
引用是经过"$ref"来表示
引用 | 描述 |
---|---|
"$ref":".." | 上一级 |
"$ref":"@" | 当前对象,也就是自引用 |
"$ref":"$" | 根对象 |
"$ref":"$.children.0" | 基于路径的引用,至关于 root.getChildren().get(0) |
{"$ref":"../.."} | 引用父对象的父对象 |
重复引用
指一个对象重复出现屡次
循环引用
指你内心有我,我内心有你(互相引用),这个问题比较严重,若是处理很差就会出现StackOverflowError异常
举例说明
重复引用
List<Object> list = new ArrayList<>(); Object obj = new Object(); list.add(obj); list.add(obj);
循环引用
// 循环引用的特殊状况,自引用 Map<String,Object> map = new HashMap<>(); map.put("map",map); // // map1引用了map2,而map2又引用map1,致使循环引用 Map<String,Object> map1 = new HashMap<>(); Map<String,Object> map2 = new HashMap<>(); map1.put("map",map2); map2.put("map",map1);
简单说,重复引用就是一个集合/对象中的多个元素/属性同时引用同一对象,循环引用就是集合/对象中的多个元素/属性存在相互引用致使循环。
循环引用会触发的问题
暂时不说重复引用,单说循环引用。
通常来讲,存在循环引用问题的集合/对象在序列化时(好比Json化),若是不加以处理,会触发StackOverflowError异常。
分析缘由:当序列化引擎解析map1时,它发现这个对象持有一个map2的引用,转而去解析map2。解析map2时,发现他又持有map1的引用,又转回map1。如此产生StackOverflowError异常。
FastJson对重复/循环引用的处理
首先,fastjson做为一款序列化引擎,不可避免的会遇到循环引用的问题,为了不StackOverflowError异常,fastjson会对引用进行检测。
若是检测到存在重复/循环引用的状况,fastjson默认会以“引用标识”代替同一对象,而非继续循环解析致使StackOverflowError。
如下文两例说明,查看json化后的输出
1.重复引用 JSON.toJSONString(list)
[ {}, //obj的实体 { "$ref": "$[0]" //对obj的重复引用的处理 } ]
2.循环引用 JSON.toJSONString(map1)
{ // map1的key:value对 "map": { // map2的key:value对 "map": { // 指向map1,对循环引用的处理 "$ref": ".." } } }
重复引用的解决方法
1.单个关闭 JSON.toJSONString(object, SerializerFeature.DisableCircularReferenceDetect); 2.全局配置关闭 JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
局部的
JSON.toJSONString(object, SerializerFeature.DisableCircularReferenceDetect);
全局的
普通的spring项目的话,用xml配置
<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"> <property name="supportedMediaTypes"> <array> <value>text/html;charset=UTF-8</value> </array> </property> <property name="features"> <array> <value>WriteMapNullValue</value> <value>WriteNullStringAsEmpty</value> <!-- 全局关闭循环引用检查,最好是不要关闭,否则有可能会StackOverflowException --> <value>DisableCircularReferenceDetect</value> </array> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
若是springboot的话
public class FastJsonHttpMessageConverterEx extends FastJsonHttpMessageConverter{ public FastJsonHttpMessageConverterEx(){ //在这里配置fastjson特性(全局设置的) FastJsonConfig fastJsonConfig = new FastJsonConfig(); //fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss"); //自定义时间格式 //fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue); //正常转换null值 //fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect); //关闭循环引用 this.setFastJsonConfig(fastJsonConfig); } @Override protected boolean supports(Class<?> clazz) { return super.supports(clazz); } } @Configuration public class WebMvcConfigurer extends WebMvcConfigurerAdapter { ..... @Bean public FastJsonHttpMessageConverterEx fastJsonHttpMessageConverterEx(){ return new FastJsonHttpMessageConverterEx(); } }
配置这个DisableCircularReferenceDetect的做用是:决定了生成的“多个”JSON对象中,是否加载被引用的同一个对象的数据。
开启和关闭FastJson的“循环引用检测”特性的对比
FastJson提供了SerializerFeature.DisableCircularReferenceDetect这个序列化选项,用来关闭引用检测。关闭引用检测后,重复引用对象时就不会被$ref代替,可是在循环引用时也会致使StackOverflowError异常。
避免重复引用序列化时显示$ref
在编码时,使用新对象为集合或对象赋值,而非使用同一对象
不要在多处引用同一个对象,这能够说是一种java编码规范,须要时刻注意。
不要关闭FastJson的引用检测来避免显示$ref
引用检测是FastJson提供的一种避免运行时异常的优良机制,若是为了不在重复引用时显示$ref而关闭它,会有很大可能致使循环引用时发生StackOverflowError异常。这也是FastJson默认开启引用检测的缘由。
循环引用的解决方法:
1.若是你前端用不到这个属性在该属性的get方法上加上注解@JSONField(serialize=false), 这样该属性就不会被序列化出来,这个也能够解决重复引用 2.修改表结构,出现循环引用了就是一个很失败的结构了,否则准备迎接StackOverflowError异常。
对应输出结果以下:
一、useSingleQuotes:
二、writeMapNullValue:
三、useISO8601DateFormat:
四、writeNullListAsEmpty:
五、writeNullStringAsEmpty:
六、prettyFormat:
七、writeDateUseDateFormat:
八、beanToArray:
九、自定义组合:showJsonBySelf:
此时完整的输出以下:
{"a":"a","b":2,"c":true,"d":"d","date":1473839656840,"e":"","list":[{"add":"广州","id":3,"name":"用户3","old":"33"},{"add":"上海","id":2,"name":"用户2","old":"22"},null,{"add":"北京","id":1,"name":"用户1","old":"11"}],"map":{"list":[{"$ref":"$.list[0]"},{"$ref":"$.list[1]"},null,{"$ref":"$.list[3]"}],"user3":{"$ref":"$.list[0]"},"mapz":"mapz","mapo":"mapo","mapa":"mapa","user1":{"$ref":"$.list[3]"}}}
{
"a":"a",
"b":2,
"c":true,
"d":"d",
"date":1473839656840,
"e":"",
"f":"",
"list":[
{
"add":"广州",
"id":3,
"name":"用户3",
"old":"33"
},
{
"add":"上海",
"id":2,
"name":"用户2",
"old":"22"
},
null,
{
"add":"北京",
"id":1,
"name":"用户1",
"old":"11"
}
],
"map":{
"list":[
{
"add":"广州",
"id":3,
"name":"用户3",
"old":"33"
},
{
"add":"上海",
"id":2,
"name":"用户2",
"old":"22"
},
null,
{
"add":"北京",
"id":1,
"name":"用户1",
"old":"11"
}
],
"user4":null,
"user3":{
"add":"广州",
"id":3,
"name":"用户3",
"old":"33"
},
"mapz":"mapz",
"mapo":"mapo",
"mapa":"mapa",
"user1":{
"add":"北京",
"id":1,
"name":"用户1",
"old":"11"
}
}
}
若是要被序列化的对象含有一个date属性或者多个date属性按照相同的格式序列化日期的话,那咱们可使用下面的语句实现:
在应用的的Main方法体里配置全局参数:
JSONObject.DEFFAULT_DATE_FORMAT="yyyy-MM-dd";//设置日期格式
或者使用时传递配置参数
JSONObject.toJSONString(resultMap, SerializerFeature.WriteMapNullValue,SerializerFeature.DisableCircularReferenceDetect,SerializerFeature.WriteDateUseDateFormat);
可是上面的解决方案面临一个问题,若是不知足上面的条件(多个date属性,并且须要按照不定的格式序列化这些日期属性),那么咱们就须要另辟蹊径,使用fastjson的特性来完成:
@JSONField(format="yyyyMMdd") private Date date; @JSONField(format="yyyy-MM-dd HH:mm:ss") private Date date1;
若是但愿DTO转换输出的是下划线风格(fastjson默认驼峰风格),请使用:
@JSONField(name="service_name") private String serviceName;
想要全局配置的话,请在Main方法体中设置:
//先执行static代码块,再执行该方法 //是否输出值为null的字段,默认为false JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteMapNullValue.getMask(); //数值字段若是为null,输出为0,而非null JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteNullNumberAsZero.getMask(); //List字段若是为null,输出为[],而非null JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteNullListAsEmpty.getMask(); //字符类型字段若是为null,输出为 "",而非null JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteNullStringAsEmpty.getMask()
PS:
public enum SerializerFeature { QuoteFieldNames,//输出key时是否使用双引号,默认为true /** * */ UseSingleQuotes,//使用单引号而不是双引号,默认为false /** * */ WriteMapNullValue,//是否输出值为null的字段,默认为false /** * */ WriteEnumUsingToString,//Enum输出name()或者original,默认为false /** * */ UseISO8601DateFormat,//Date使用ISO8601格式输出,默认为false /** * @since 1.1 */ WriteNullListAsEmpty,//List字段若是为null,输出为[],而非null /** * @since 1.1 */ WriteNullStringAsEmpty,//字符类型字段若是为null,输出为"",而非null /** * @since 1.1 */ WriteNullNumberAsZero,//数值字段若是为null,输出为0,而非null /** * @since 1.1 */ WriteNullBooleanAsFalse,//Boolean字段若是为null,输出为false,而非null /** * @since 1.1 */ SkipTransientField,//若是是true,类中的Get方法对应的Field是transient,序列化时将会被忽略。默认为true /** * @since 1.1 */ SortField,//按字段名称排序后输出。默认为false /** * @since 1.1.1 */ @Deprecated WriteTabAsSpecial,//把\t作转义输出,默认为false /** * @since 1.1.2 */ PrettyFormat,//结果是否格式化,默认为false /** * @since 1.1.2 */ WriteClassName,//序列化时写入类型信息,默认为false。反序列化是需用到 /** * @since 1.1.6 */ DisableCircularReferenceDetect,//消除对同一对象循环引用的问题,默认为false /** * @since 1.1.9 */ WriteSlashAsSpecial,//对斜杠'/'进行转义 /** * @since 1.1.10 */ BrowserCompatible,//将中文都会序列化为\uXXXX格式,字节数会多一些,可是能兼容IE 6,默认为false /** * @since 1.1.14 */ WriteDateUseDateFormat,//全局修改日期格式,默认为false。JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd";JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat); /** * @since 1.1.15 */ NotWriteRootClassName,//暂不知,求告知 /** * @since 1.1.19 */ DisableCheckSpecialChar,//一个对象的字符串属性中若是有特殊字符如双引号,将会在转成json时带有反斜杠转移符。若是不须要转义,可使用这个属性。默认为false /** * @since 1.1.35 */ BeanToArray //暂不知,求告知 ; private SerializerFeature(){ mask = (1 << ordinal()); } private final int mask; public final int getMask() { return mask; } public static boolean isEnabled(int features, SerializerFeature feature) { return (features & feature.getMask()) != 0; } public static int config(int features, SerializerFeature feature, boolean state) { if (state) { features |= feature.getMask(); } else { features &= ~feature.getMask(); } return features; } }
参照项目github地址: - https://github.com/gubaijin/buildmavenweb