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