Android使用Gson的總結

先說一下gson的使用方法,可以參考這篇帖子,寫的很詳細,感謝作者,以下內容是根據原作者帖子進行總結的自己的筆記

簡要總結

  1. 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
  1. 使用@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)中出現任意一個時均可以得到正確的結果

  1. 泛型擦除
    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());//解析成集合
  1. 針對服務器返回數據的封裝
    有下面兩種數據,我們需要的是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;

這樣就解決了服務器返回數據的封裝

  1. Gson的一些配置
Gson gson = new GsonBuilder()
        //序列化null
        .serializeNulls()
        // 設置日期時間格式,另有2個重載方法
        // 在序列化和反序化時均生效
        .setDateFormat("yyyy-MM-dd")
        // 禁此序列化內部類
        .disableInnerClassSerialization()
        //生成不可執行的Json(多了 )]}' 這4個字符)
        .generateNonExecutableJson()
        //禁止轉義html標簽
        .disableHtmlEscaping()
        //格式化輸出
        .setPrettyPrinting()
        .create();
  1. 基于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方法傳進去的字符串不能含有轉義?等翻了源碼再確定一下這個問題

總結

通過踩坑,總結幾點:

  1. 在做適配的時候,首先要排除系統問題,認真記錄一下操作系統的版本
  2. 做最小化測試,首先從最簡單的開始排除;
    例如此坑中,傳遞的base64字符串特別大,算了一下大概有8萬個字符,剛開始懷疑是字符太多,就做實驗,寫了一個循環,使用StringBuilder每次添加1萬個字符進去,發現到10萬都沒問題,這樣就排除了大小限制;然后再就是排除特殊符號,比如'' /r/n \r\n 等等;一個個去排除;最后發現就是特殊字符引起的.
    3.認真做好每次踩坑的筆記
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,065評論 25 708
  • 本文為作者根據日常使用結合Gson源碼注釋及wiki所作的原創內容,轉載請注明出處。本文鏈接:http://www...
    怪盜kidou閱讀 379,101評論 210 1,106
  • 轉載:http://www.lxweimin.com/p/e740196225a4本文為作者根據日常使用結合Gson...
    朝花夕拾不起來閱讀 992評論 0 1
  • 。坦然是一種失意后的樂觀。 坦然是沮喪時的一點調適。 坦然是平淡中的自信。 忽然想起泰戈爾的最有名的一句詩:“天空...
    絢爛的陽光閱讀 655評論 0 0
  • 這是一本關于如何應對快速變化世界的產品策略書,它沒有列出1,2,3來告訴你如何去做,它只是解釋了設計和產品的關系,...
    PM小森閱讀 437評論 0 2