一、fastjson介紹
在前后端數據傳輸交互中,經常會遇到字符串(String)與json
,XML
等格式相互轉換與解析,其中json
以跨語言,跨前后端的優點在開發中被頻繁使用,基本上可以說是標准的數據交換格式。fastjson 是一個java語言編寫的高性能且功能完善的JSON庫,它采用一種“假定有序快速匹配”的算法,把JSON Parse
的性能提升到了極致。它的接口簡單易用,已經被廣泛使用在緩存序列化,協議交互,Web輸出等各種應用場景中。
FastJson是啊里巴巴的的開源庫,用於對JSON格式的數據進行解析和打包。
特點如下:
(1)能夠支持將java bean序列化成JSON
字符串,也能夠將JSON字符串反序列化成Java bean。
(2)顧名思義,fastjson
操作 JSON
的速度是非常快的。
(3)無其他包的依賴。
(4)使用比較方便。
二、fastjson使用
在Maven項目中使用fastjson
庫,需要提前在Maven的配置文件中添加此fastjson
包的依賴,如pom.xml
文件。
添加下面的依賴:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<!--(起碼1.2.48以上)因為這個版本一下存在漏洞-->
<version>版本根據自己需要</version>
</dependency>
單獨練習使用的話,下載對應的jar
導入項目即可.
jar下載地址:fastjson-1.2.58.jar
三、fastjson 常用 API
fastjson API
入口類是com.alibaba.fastjson.JSON
,常用的序列化操作都可以在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。
四、fastjson使用演示
測試類准備
User類
public class User {
private String username;
private String password;
public User(){}
public User(String username,String password){
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + "]";
}
}
UserGroup類:這里類里面包含User類的集合。
import java.util.ArrayList;
import java.util.List;
public class UserGroup {
private String name;
private List<User> users = new ArrayList<User>();
public UserGroup(){}
public UserGroup(String name,List<User> users){
this.name = name;
this.users = users;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
@Override
public String toString() {
return "UserGroup [name=" + name + ", users=" + users + "]";
}
}
1.java類轉換為json字符串
package javabasic.json;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: fastjson API使用練習
* @Author: ggf
* @Date: 2020/01/11
*/
public class FastJsonTest {
/**
* java對象轉換為json字符串
*/
@Test
public void objToJson() {
// 簡單對象轉換
User user = new User("ggf","123456");
// 調用toJSONString()
String userJson = JSON.toJSONString(user);
System.out.println("java類轉換為json串:" + userJson);
// 集合(List<E>)轉json串
User user1 = new User("zhangsan", "123456");
User user2 = new User("lisi", "654321");
//創建集合存儲對象
List<User> users = new ArrayList<User>();
users.add(user1);
users.add(user2);
// 調用toJSONString()轉換對象
String usersjson = JSON.toJSONString(users);
System.out.println("集合(List<E>)轉json串:" + usersjson);
// 復雜java類(類中包含集合對象)轉換json串
UserGroup userGroup = new UserGroup("userGroup", users);
// 調用toJSONString()轉換對象
String userGroupJson = JSON.toJSONString(userGroup);
System.out.println("復雜java類(類中包含集合對象)轉換json串:" + userGroupJson);
}
}
輸出結果:
java類轉換為json串:{"password":"123456","username":"ggf"}
集合(List<E>)轉json串:[{"password":"123456","username":"zhangsan"},{"password":"654321","username":"lisi"}]
復雜java類(類中包含集合對象)轉換json串:{"name":"userGroup","users":[{"password":"123456","username":"zhangsan"},{"password":"654321","username":"lisi"}]}
2.json字符串轉為java類
package javabasic.json;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: fastjson API使用練習
* @Author: ggf
* @Date: 2020/01/11
*/
public class FastJsonTest {
/**
* json字符串轉為java類
* 注:字符串中使用雙引號需要轉義 (" --> \"),這里使用的是單引號,易讀性會好很多。
* json串以“{}”包裹,轉換為java類時,使用:parseObject();
* json串以“[]”包裹,轉換為java類時,使用:parseArray();
*/
@Test
public void jsonToObj() {
/* json字符串轉簡單java對象
* 字符串:{"password":"123456","username":"dmego"}
*/
String jsonStr1 = "{'password':'123456','username':'ggf'}";
// 調用parseObject()
User user = JSON.parseObject(jsonStr1, User.class);
System.out.println("json字符串轉簡單java對象:"+user.toString());
/*
* json字符串轉List<Object>對象
* 字符串:[{"password":"123123","username":"zhangsan"},
* {"password":"321321","username":"lisi"}]
*/
String jsonStr2 = "[{'password':'123123','username':'zhangsan'},{'password':'321321','username':'lisi'}]";
// 調用parseArray()將字符串轉為集合
List<User> users = JSON.parseArray(jsonStr2, User.class);
System.out.println("json字符串轉List<Object>對象:"+users.toString());
/*json字符串轉復雜java對象
* 字符串:{"name":"userGroup","users":[{"password":"123123","username":"zhangsan"},{"password":"321321","username":"lisi"}]}
* */
String jsonStr3 = "{'name':'userGroup','users':[{'password':'123123','username':'zhangsan'},{'password':'321321','username':'lisi'}]}";
UserGroup userGroup = JSON.parseObject(jsonStr3, UserGroup.class);
System.out.println("json字符串轉復雜java對象:"+userGroup);
}
}
輸出結果:
json字符串轉簡單java對象:User [username=ggf, password=123456]
json字符串轉List<Object>對象:[User [username=zhangsan, password=123123], User [username=lisi, password=321321]]
json字符串轉復雜java對象:UserGroup [name=userGroup, users=[User [username=zhangsan, password=123123], User [username=lisi, password=321321]]]
五、fastjson實際開發應用
1.對復雜的json串轉為java類
首先有這么一個json字符串,這是一個羊肉湯的菜譜,數據來源於《聚合數據》
{
"resultcode":"200",
"reason":"Success",
"result":{
"data":[
{
"id":"6269",
"title":"羊肉湯",
"tags":"增強抵抗力;煮;家常菜;湯;魯菜",
"imtro":"鄒城人有喝羊湯的習慣,春夏秋冬羊湯館總斷不了食客,春秋天氣候干燥要喝,夏天入伏要喝“伏羊湯”,陰冷的冬季尤其要喝碗羊湯才夠溫暖。以至於邀友喝羊湯成為了禮儀;“二哥,晚上咱們喝羊湯去”。鄒城的羊湯鋪遍地開花,以至於單縣羊湯、滕州羊湯在鄒城都沒有了用武之地。我們這里的羊湯做法是最純的,基本不放煮肉的香料,就用羊骨和羊肉煮成,“肉嫩湯濃”是其特色。 煮羊湯要先煮羊骨,把羊骨斬成大段焯水后放一點羊板油用細火煮,煮到湯白味濃時放入羊肉。羊肉煮到用筷子能輕松插穿時就要撈出,久煮的話羊肉過爛就失去了軟嫩的口感。 碗里放入蔥花或蒜粒,調入精鹽,放入切的薄薄的羊肉片。把燒的滾開的羊湯盛到碗里,灑上香菜,再挖上一匙子香辣的用羊油潑成的辣椒油,一個字“香”!",
"ingredients":"山羊肉,500g;羊骨,1000g",
"burden":"生姜,適量;精鹽,適量;香菜,適量;大蔥,適量;辣椒油,適量;羊板油,適量",
"albums":[
"http:\/\/juheimg.oss-cn-hangzhou.aliyuncs.com\/cookbook\/t\/7\/6269_379835.jpg"
],
"steps":[
{
"img":"http:\/\/juheimg.oss-cn-hangzhou.aliyuncs.com\/cookbook\/s\/63\/6269_95d65e77b58a1b6b.jpg",
"step":"1.羊脊骨洗凈用刀斬成段。"
},
{
"img":"http:\/\/juheimg.oss-cn-hangzhou.aliyuncs.com\/cookbook\/s\/63\/6269_a8136c10401a1643.jpg",
"step":"2.煮鍋里倒入清水,放入羊脊骨,羊肉煮開后撈出。"
},
{
"img":"http:\/\/juheimg.oss-cn-hangzhou.aliyuncs.com\/cookbook\/s\/63\/6269_c7b1c9fc85ddc6de.jpg",
"step":"3.煮鍋里倒入開水,放入羊脊骨生姜塊大火煮開后改小火。"
},
{
"img":"http:\/\/juheimg.oss-cn-hangzhou.aliyuncs.com\/cookbook\/s\/63\/6269_2b284dc30b4f0875.jpg",
"step":"4.小火煮40分鍾,煮至湯色發白。"
},
{
"img":"http:\/\/juheimg.oss-cn-hangzhou.aliyuncs.com\/cookbook\/s\/63\/6269_c7ade6439eb2db5a.jpg",
"step":"5.放入羊肉,加入適量的羊板油小火煮30分鍾。"
},
{
"img":"http:\/\/juheimg.oss-cn-hangzhou.aliyuncs.com\/cookbook\/s\/63\/6269_579748e3b0f15963.jpg",
"step":"6.撈出煮好的羊肉,晾涼后切薄片。"
},
{
"img":"http:\/\/juheimg.oss-cn-hangzhou.aliyuncs.com\/cookbook\/s\/63\/6269_1550e6f127aa1077.jpg",
"step":"7.碗里放入蔥花,調入精鹽。"
},
{
"img":"http:\/\/juheimg.oss-cn-hangzhou.aliyuncs.com\/cookbook\/s\/63\/6269_a2c965d77b96da70.jpg",
"step":"8.放入羊肉片,把滾開的羊湯倒入碗里灑上香菜末。"
},
{
"img":"http:\/\/juheimg.oss-cn-hangzhou.aliyuncs.com\/cookbook\/s\/63\/6269_eea9b807d1dc5995.jpg",
"step":"9.可以根據喜好調入陳醋放入蒜粒,最后調入辣椒油即可。"
}
]
}
],
"totalNum":"9",
"pn":0,
"rn":"1"
},
"error_code":0
}
要想解析這種復雜的字符串,把它轉換成java類的話,首先得先定義好與之相符的java POJO
對象,從上面的json字符串組成來看,我們可以拆分出來四個bean:
最外層的響應:ResponseData
返回結果:ResultBean
數據:DataBean
做菜步驟:StepsBean
將拿到的json字符串數據,用GsonFormat工具來生成java類。
GsonFormat工具的使用可參考該文章:https://www.cnblogs.com/1024zy/p/6370305.html
package javabasic.json;
import java.util.List;
/**
* @Description: 菜譜數據響應體
* 使用GsonFormat功能生成
* @Author: ggf
* @Date: 2020/01/11
*/
public class ResponseData {
private String resultcode;
private String reason;
private ResultBean result;
private int error_code;
public String getResultcode() {
return resultcode;
}
public void setResultcode(String resultcode) {
this.resultcode = resultcode;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public ResultBean getResult() {
return result;
}
public void setResult(ResultBean result) {
this.result = result;
}
public int getError_code() {
return error_code;
}
public void setError_code(int error_code) {
this.error_code = error_code;
}
@Override
public String toString() {
return "ResponseData{" +
"resultcode='" + resultcode + '\'' +
", reason='" + reason + '\'' +
", result=" + result +
", error_code=" + error_code +
'}';
}
public static class ResultBean {
private String totalNum;
private int pn;
private String rn;
private List<DataBean> data;
public String getTotalNum() {
return totalNum;
}
public void setTotalNum(String totalNum) {
this.totalNum = totalNum;
}
public int getPn() {
return pn;
}
public void setPn(int pn) {
this.pn = pn;
}
public String getRn() {
return rn;
}
public void setRn(String rn) {
this.rn = rn;
}
public List<DataBean> getData() {
return data;
}
public void setData(List<DataBean> data) {
this.data = data;
}
@Override
public String toString() {
return "ResultBean{" +
"totalNum='" + totalNum + '\'' +
", pn=" + pn +
", rn='" + rn + '\'' +
", data=" + data +
'}';
}
public static class DataBean {
private String id;
private String title;
private String tags;
private String imtro;
private String ingredients;
private String burden;
private List<String> albums;
private List<StepsBean> steps;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTags() {
return tags;
}
public void setTags(String tags) {
this.tags = tags;
}
public String getImtro() {
return imtro;
}
public void setImtro(String imtro) {
this.imtro = imtro;
}
public String getIngredients() {
return ingredients;
}
public void setIngredients(String ingredients) {
this.ingredients = ingredients;
}
public String getBurden() {
return burden;
}
public void setBurden(String burden) {
this.burden = burden;
}
public List<String> getAlbums() {
return albums;
}
public void setAlbums(List<String> albums) {
this.albums = albums;
}
public List<StepsBean> getSteps() {
return steps;
}
public void setSteps(List<StepsBean> steps) {
this.steps = steps;
}
@Override
public String toString() {
return "DataBean{" +
"id='" + id + '\'' +
", title='" + title + '\'' +
", tags='" + tags + '\'' +
", imtro='" + imtro + '\'' +
", ingredients='" + ingredients + '\'' +
", burden='" + burden + '\'' +
", albums=" + albums +
", steps=" + steps +
'}';
}
public static class StepsBean {
private String img;
private String step;
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
public String getStep() {
return step;
}
public void setStep(String step) {
this.step = step;
}
@Override
public String toString() {
return "StepsBean{" +
"img='" + img + '\'' +
", step='" + step + '\'' +
'}';
}
}
}
}
}
對應的實體類創建后,接下來就可以使用fastjson中的方法將json串轉換成對象使用了
package javabasic.json;
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.util.IOUtils;
import org.junit.Test;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: fastjson API使用練習
* @Author: ggf
* @Date: 2020/01/11
*/
public class FastJsonTest {
/**
*將復雜的json串轉換為java類
*/
@Test
public void jsonToComplexObj() {
// 讀取類路徑下的caipu.json文件,這里使用了第三方工具hutool進行讀取json文件
// 工具類參見:https://hutool.cn/docs/#/
String jsonStr = FileUtil.readUtf8String("./javabasic/json/caipu.json");
System.out.println(jsonStr);
// 轉換為java類
ResponseData resp = JSON.parseObject(jsonStr, ResponseData.class);
System.out.println(resp);
// 通過對象操作數據
// 獲取響應碼resultcode
System.out.println(resp.getResultcode());
// 獲取響應數據
ResponseData.ResultBean result = resp.getResult();
System.out.println("result響應數據:" + result);
}
}
輸出結果
ResponseData{resultcode='200', reason='Success', result=ResultBean{totalNum='9', pn=0, rn='1', data=[DataBean{id='6269', title='羊肉湯', tags='增強抵抗力;煮;家常菜;湯;魯菜', imtro='鄒城人有喝羊湯的習慣,春夏秋冬羊湯館總斷不了食客,春秋天氣候干燥要喝,夏天入伏要喝“伏羊湯”,陰冷的冬季尤其要喝碗羊湯才夠溫暖。以至於邀友喝羊湯成為了禮儀;“二哥,晚上咱們喝羊湯去”。鄒城的羊湯鋪遍地開花,以至於單縣羊湯、滕州羊湯在鄒城都沒有了用武之地。我們這里的羊湯做法是最純的,基本不放煮肉的香料,就用羊骨和羊肉煮成,“肉嫩湯濃”是其特色。 煮羊湯要先煮羊骨,把羊骨斬成大段焯水后放一點羊板油用細火煮,煮到湯白味濃時放入羊肉。羊肉煮到用筷子能輕松插穿時就要撈出,久煮的話羊肉過爛就失去了軟嫩的口感。 碗里放入蔥花或蒜粒,調入精鹽,放入切的薄薄的羊肉片。把燒的滾開的羊湯盛到碗里,灑上香菜,再挖上一匙子香辣的用羊油潑成的辣椒油,一個字“香”!', ingredients='山羊肉,500g;羊骨,1000g', burden='生姜,適量;精鹽,適量;香菜,適量;大蔥,適量;辣椒油,適量;羊板油,適量', albums=[http://juheimg.oss-cn-hangzhou.aliyuncs.com/cookbook/t/7/6269_379835.jpg], steps=[StepsBean{img='http://juheimg.oss-cn-hangzhou.aliyuncs.com/cookbook/s/63/6269_95d65e77b58a1b6b.jpg', step='1.羊脊骨洗凈用刀斬成段。'}, StepsBean{img='http://juheimg.oss-cn-hangzhou.aliyuncs.com/cookbook/s/63/6269_a8136c10401a1643.jpg', step='2.煮鍋里倒入清水,放入羊脊骨,羊肉煮開后撈出。'}, StepsBean{img='http://juheimg.oss-cn-hangzhou.aliyuncs.com/cookbook/s/63/6269_c7b1c9fc85ddc6de.jpg', step='3.煮鍋里倒入開水,放入羊脊骨生姜塊大火煮開后改小火。'}, StepsBean{img='http://juheimg.oss-cn-hangzhou.aliyuncs.com/cookbook/s/63/6269_2b284dc30b4f0875.jpg', step='4.小火煮40分鍾,煮至湯色發白。'}, StepsBean{img='http://juheimg.oss-cn-hangzhou.aliyuncs.com/cookbook/s/63/6269_c7ade6439eb2db5a.jpg', step='5.放入羊肉,加入適量的羊板油小火煮30分鍾。'}, StepsBean{img='http://juheimg.oss-cn-hangzhou.aliyuncs.com/cookbook/s/63/6269_579748e3b0f15963.jpg', step='6.撈出煮好的羊肉,晾涼后切薄片。'}, StepsBean{img='http://juheimg.oss-cn-hangzhou.aliyuncs.com/cookbook/s/63/6269_1550e6f127aa1077.jpg', step='7.碗里放入蔥花,調入精鹽。'}, StepsBean{img='http://juheimg.oss-cn-hangzhou.aliyuncs.com/cookbook/s/63/6269_a2c965d77b96da70.jpg', step='8.放入羊肉片,把滾開的羊湯倒入碗里灑上香菜末。'}, StepsBean{img='http://juheimg.oss-cn-hangzhou.aliyuncs.com/cookbook/s/63/6269_eea9b807d1dc5995.jpg', step='9.可以根據喜好調入陳醋放入蒜粒,最后調入辣椒油即可。'}]}]}, error_code=0}
2.對json串的操作
在實際開發中,我們經常要對接口返回的json數據,進行操作,獲取里面的某些數據。還是以上面的json字符串為例,使用fastjson,對json字符串進行操作。
package javabasic.json;
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.util.IOUtils;
import org.junit.Test;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: fastjson API使用練習
* @Author: ggf
* @Date: 2020/01/11
*/
public class FastJsonTest {
/**
* 對json串的操作
*/
@Test
public void operateJson() {
// 讀取本地json文本
String jsonStr = FileUtil.readUtf8String("./javabasic/json/caipu.json");
// 創建json對象
JSONObject jsonObj = JSONObject.parseObject(jsonStr);
// 操作json內容
// 獲取響應碼resultcode
System.out.println(jsonObj.get("resultcode"));
// 獲取響應信息reason
System.out.println(jsonObj.getString("reason"));
// 獲取data
JSONObject resJsonObj = (JSONObject)jsonObj.get("result");
System.out.println(resJsonObj.getString("data"));
}
}
輸出結果
200
Success
[{"albums":["http://juheimg.oss-cn-hangzhou.aliyuncs.com/cookbook/t/7/6269_379835.jpg"],
.......以下內容省略
"}]
六、fastjson漏洞問題
可參考文章:https://www.cnblogs.com/chaos-li/p/11139992.html
真實項目中使用建設使用版本大於:1.2.45
七、fastjson踩坑
序列化的類必須有一個無參構造方法
被序列化的類需要有一個無參的構造方法。否則會報錯
Exception in thread "main" com.alibaba.fastjson.JSONException: default constructor not found. class User
如果你沒有重寫構造方法,那么每個類都自帶一個無參的構造方法,但是如果你重寫了一個有參的構造方法,那么默認的無參構造方法會被覆蓋,這時候就需要你手動寫一個無參的構造方法進去。所以我建議保險起見,需要被json序列化的類最好都手動寫一個無參的構造方法進去。
在低版本中轉換的時候會直接拋以上異常信息(測試版本:fastjson-1.1.12)。但是高版本(fastjson-1.2.58)就不會報錯。
建議在定義javabean時都把無參和有參定義。
【參考文章】
https://www.cnblogs.com/dmego/p/9033080.html
https://blog.csdn.net/zgzczzw/article/details/72330190
https://blog.csdn.net/JLoveforever/article/details/79885485