先說一下gson的使用方法,可以參考這篇帖子,寫的很詳細,感謝作者,以下內容是根據原作者帖子進行總結的自己的筆記
簡要總結
- Gson提供了fromJson() 和toJson() 兩個直接用于解析和生成的方法,前者實現反序列化,后者實現了序列化。同時每個方法都提供了重載方法,我常用的總共有5個。
Gson gson = new Gson();
int i = gson.fromJson("100", int.class); //100
double d = gson.fromJson("\"99.99\"", double.class); //99.99
boolean b = gson.fromJson("true", boolean.class); // true
String str = gson.fromJson("String", String.class); // String
- 使用@SerializedName 注解重新命名字段名稱
這對于使用PHP作為后臺開發語言時很常見的情況,php和js在命名時一般采用下劃線風格,而Java中一般采用的駝峰法,讓后臺的哥們改吧 前端和后臺都不爽,但要自己使用下劃線風格時我會感到不適應,怎么辦?難到沒有兩全齊美的方法么?
我們知道Gson在序列化和反序列化時需要使用反射,說到反射就不得不想到注解,一般各類庫都將注解放到annotations包下,打開源碼在com.google.gson包下果然有一個annotations,里面有一個SerializedName的注解類,這應該就是我們要找的。
那么對于json中email_address這個屬性對應POJO的屬性則變成
@SerializedName("email_address")
public String emailAddress;
對于有多個字段都需要重新命名時,SerializedName注解提供了兩個屬性,上面用到了其中一個,別外還有一個屬性alternate,接收一個String數組。
注:alternate需要2.4版本
@SerializedName(value = "emailAddress", alternate = {"email", "email_address"})
public String emailAddress;
當上面的三個屬性(email_address、email、emailAddress)中出現任意一個時均可以得到正確的結果
- 泛型擦除
Gson解析數組的時候,比較方便,例如
["Android","Java","PHP"]
直接解析
Gson gson = new Gson();
String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);
但對于List將上面的代碼中的 String[].class 直接改為 List<String>.class 是行不通的。對于Java來說List<String> 和List<User> 這倆個的字節碼文件只一個那就是List.class,這是Java泛型使用時要注意的問題 泛型擦除。
為了解決的上面的問題,Gson為我們提供了TypeToken來實現對泛型的支持,所以當我們希望使用將以上的數據解析為List<String>時需要這樣寫。
Gson gson = new Gson();
String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);//解析成數組
List<String> list = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());//解析成集合
- 針對服務器返回數據的封裝
有下面兩種數據,我們需要的是data,code只用一次,message基本不用,那么針對data,第一個是bean,第二個則是數組,為了形象,暫且理解為第一個是User,第二個是List<User>
"code":"0","message":"success","data":{}}
{"code":"0","message":"success","data":[]}
那么不適用泛型封裝的時候,我們的字典類一定是這么寫
public class UserResponse {
public int code;
public String message;
public User data;
}
public class UserResponseList {
public int code;
public String message;
public List<User> list;
}
藍后,我們的解析是這么寫
Gson gson = new Gson();
//解析bean
UserResult userResult = gson.fromJson(json,UserResponse .class);
User user = userResult.data;
//解析集合
UserResponseList userList= gson.fromJson(json,UserResponseList .class);
List<User> users = userList.data;
所以如果一兩個還行,等真正上項目的時候,那就無語了;那么使用泛型針對返回數據進行封裝了?
可以看到,公共的字段是code和message,也就是說每個后臺返回來的json數據格式都是這種格式,我們就可以把公共的部分抽取出來,至于data我們不知道到底返回的是單獨的字典,還是集合,還是數組,所以采用泛型
public class Result<T> {
public int code;
public String message;
public T data;
}
這時候對于data字段是User時則可以寫為 Result<User> ,當是個列表的時候為 Result<List<User>>,其它同理。
解析的時候
Type userType = new TypeToken<Result<User>>(){}.getType();
Result<User> userResult = gson.fromJson(json,userType);
User user = userResult.data;
Type userListType = new TypeToken<Result<List<User>>>(){}.getType();
Result<List<User>> userListResult = gson.fromJson(json,userListType);
List<User> users = userListResult.data;
這樣就解決了服務器返回數據的封裝
- Gson的一些配置
Gson gson = new GsonBuilder()
//序列化null
.serializeNulls()
// 設置日期時間格式,另有2個重載方法
// 在序列化和反序化時均生效
.setDateFormat("yyyy-MM-dd")
// 禁此序列化內部類
.disableInnerClassSerialization()
//生成不可執行的Json(多了 )]}' 這4個字符)
.generateNonExecutableJson()
//禁止轉義html標簽
.disableHtmlEscaping()
//格式化輸出
.setPrettyPrinting()
.create();
- 基于Gson系列化json的踩坑
在三星note2(Android4.3)手機上面,使用webView.loadUrl(url)這個方法給js回傳一個String類型的base64圖片,發現js根本就沒有拿到這個base64,經過debug,發現使用Gson.toJson方法,生成的字符串自動給價格換行符\n,各種找了一圈都沒有發現問題,最后在Gson初始化這里
public static Gson getGson() {
if (null == gson) {
synchronized (GsonUtils.class) {
if (null == gson) {
GsonBuilder builder = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting();
gson = builder.setDateFormat("yyyy-MM-dd HH:mm:ss").create();//.setFieldNamingStrategy(new AnnotateNaming()).create();
}
}
}
return gson;
}
發現后面多了個.setPrettyPrinting();這個方法,意思是格式化輸出,去掉這個,\n沒有了,再次傳的時候發現問題解決
這個問題在4.4及以上沒有發現,應該是系統問題,4.3及以前的webView用的是WebKit內核,從4.4開始使用的是Chrome內核,4.3的loadUrl方法傳進去的字符串不能含有轉義?等翻了源碼再確定一下這個問題
總結
通過踩坑,總結幾點:
- 在做適配的時候,首先要排除系統問題,認真記錄一下操作系統的版本
- 做最小化測試,首先從最簡單的開始排除;
例如此坑中,傳遞的base64字符串特別大,算了一下大概有8萬個字符,剛開始懷疑是字符太多,就做實驗,寫了一個循環,使用StringBuilder每次添加1萬個字符進去,發現到10萬都沒問題,這樣就排除了大小限制;然后再就是排除特殊符號,比如'' /r/n \r\n 等等;一個個去排除;最后發現就是特殊字符引起的.
3.認真做好每次踩坑的筆記