JSON工具類的構建(後端版本)
前言
在前後端互動的選擇上,之前一直採用的是模板引擎(因為我只負責後端)。 而這次的一個算是作業吧,前後端都是我,所以就研究了一下JSON互動在java web的應用(主要是前端)。
優缺點
-
前後端耦合
- 模板引擎載入只是將jsp的互動方式移植到html上,前端檔案格式改變了,但是jsp中前後端耦合的缺點沒有改變。
- json互動中,資料通過js/jquery動態載入在頁面上,資料與頁面進行分離,頁面只是單純用於展示。
-
資料載入邏輯複用
- 模板引擎的方式中,如果有很多相似的頁面元素以及一樣的資料返回格式,那只是複製貼上大法了。
- 在模板引擎的例子中,只需要定義一套資料載入模型,傳入不同的頁面元素id以及資料則能實現邏輯複用
-
後端介面的複用 因為我的學習路線的問題,所以我開發過安卓原生一段時間。
- 模板引擎式的載入必須使用webView元件載入,且需另進行原生構建時介面也要另外構建。
- json互動,谷歌爸爸鼓勵使用json進行互動(一年多前的事,現在不清楚了),且安卓原生內建GJSON進行json解析與構建,所以可以在原生以及跨平臺的構建有很好的平衡。
對比
既然選擇了json互動的方式,而java官方據我所知是沒有內建對json的支援,Spring在Controller的層面使用RestContrller註解實現對json的支援。 但是我個人強迫症很強,我對Contrller(或者說是Presenter)的定義是
- 對使用者訪問的url作頁面的對映
- 對使用者觸發的事件進行資料的傳遞與返回 正是這兩點的定義,我需要在Service層組合統一格式的結果返回到上層,所以需要第三方json支援。 可選擇的JSON庫有很多,GSON,FastJson,Jackson,根據對比,我選擇為馬老師充值一波。
需求拆分
我初步定義的需求主要有三個
- Service傳遞統一處理結果到上層
- 無論是單一資料實體或者List型資料處理的結果是一樣的
- 處理過程是獨立的,不依賴於實體類的支援
格式設計
在網上看過很多後端返回資料的格式,很多都是返回一個處理的status以及具體的資料,而這個status是根據http狀態碼進行設定的,因為這次時間比較緊,所以我就採用了這個方案。
{
"status": "status","object": {
}
}
複製程式碼
程式碼設計
因為我希望Controller能直接拿到結果,所以構建結果的過程全放在Result類中。 而結果構建我主要分為兩種:只有狀態碼(通知處理結果)以及具有返回結果(資料顯示),而根據結果的個人也分為兩種:單個資料以及List型資料。
實際程式碼
@Getter
@Slf4j
public class ResultSet {
private JSONObject result;
public ResultSet(){
result=new JSONObject();
}
/**
* 初始化狀態碼
* @param status
*/
public void initStatus(String status){
result.put("status",status);
}
/**
* 初始化狀態碼以及返回資料
* @param status
* @param obj
*/
public void initData(String status,Object obj){
initStatus(status);
if(obj instanceof List){
List list=(List)obj;
JSONArray array=new JSONArray();
for(Object object:list){
array.add(putObjectToJSON(object));
}
result.put("object",array);
}
else {
result.put("object",putObjectToJSON(obj));
}
}
/**
* 將單個Object放入json檔案中
* @param obj
* @return
*/
private JSONObject putObjectToJSON(Object obj){
JSONObject result=new JSONObject();
Field[] fields=obj.getClass().getDeclaredFields();
for(Field field:fields){
field.setAccessible(true);
String fieldName=field.getType().getSimpleName();
if(fieldName.equals("Department")||fieldName.equals("Job")){
JSONObject tempJson=new JSONObject();
try {
Object tempObject = field.get(obj);
Field[] tempFields=tempObject.getClass().getDeclaredFields();
for(Field tempField:tempFields){
tempField.setAccessible(true);
tempJson.put(tempField.getName(),tempField.get(tempObject));
tempField.setAccessible(false);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if(fieldName.equals("Department")){
result.put("depart",tempJson);
}
else {
result.put("job",tempJson);
}
}
else {
try {
result.put(field.getName(),field.get(obj));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
field.setAccessible(false);
}
return result;
}
}
複製程式碼
程式碼思路
-
Service通過initStatus/initData傳入資料/狀態碼進行物件的生成
-
Controller通過result的getter方法獲取處理結果
-
單個資料以及List資料的處理
- 單個物件直接通過putObjectToJSON進行處理
- List資料通過物件型別判斷,向下轉型,遍歷元素形成JSONArray進行處理,對元素處理的方法也是採用putObjectToJSON
-
巢狀物件的處理
public class Employee { private int id; private Department depart; private Job job; private String name; .................. } 複製程式碼
- 通過反射獲取物件的所有成員變數型別以及對應的值
- 遇到上述bean類含有巢狀自定義物件時,遞迴生成json檔案加入到結果json中
結果演示
(左為單object型,右為List型)
不足之處
- 狀態碼的設定應該採用列舉類的賦值,能更好的約束返回的狀態碼
- 在物件轉換方面,應該採用配置掃描的方式。在配置檔案中寫入bean的包所在,在巢狀物件轉換時通過掃描配置檔案的資訊判斷
- 在巢狀物件的轉換方面,只是做了一層的巢狀轉換,更多層的沒有考慮到,之後會再重構的
- 對異常處理方面,只是簡單的輸出錯誤資訊。應該對錯誤資訊進行進一步的處理
後記
因為這次時間比較趕,從專案的立項到成品的建立花了5天時間,所以注意到很多細節,但是沒有去處理。 有想過之後有時間的話將這個工具類的細節完善起來,形成jar包供自己或者供開源。
相關連線
這只是後臺對結果的統一處理,我另外寫了一篇文章:JSON工具類的構建(前端版本),配合使用效果更佳哦~
本文首發於cartoon的部落格
轉載請註明出處:cartoonyu.github.io/cartoon-blo…