我的GitHub | 我的博客 | 我的微信 | 我的郵箱 |
---|---|---|---|
baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
implementation 'com.google.code.gson:gson:2.8.2'
通過 fromJson 反序列化為對象
普通對象
Person person = gson.fromJson(jsonString, Person.class);
Person person = gson.fromJson(jsonString, new TypeToken<Person>() {}.getType());
數組
String[] array = gson.fromJson(jsonString, String[].class);
String[] array = gson.fromJson(jsonString, new TypeToken<String[]>() {}.getType());
int[][] array = gson.fromJson(jsonString, int[][].class);
集合
List<Person> list = gson.fromJson(jsonString, new TypeToken<List<Person>>() {}.getType());//支持帶泛型
Map<String, Integer> map = gson.fromJson(jsonString2, new TypeToken<Map<String, Integer>>() {}.getType());
問題:為何要用 TypeToken
去包裝要解析的類型?為何不直接用 List<Person>.class
?
解疑:因為編譯之后,List 和 List 這寫類型的字節碼文件只一個,那就是
List.class
,這是Java泛型使用時要注意的問題:泛型擦除
。
更優雅的打印一個對象
class Fu {
protected String key = "fu";
}
class A extends Fu {
private static String NAME = "A";
private boolean isBest = true;
private int age = 28;
}
System.out.println(new Gson().toJson(new A())); //{"isBest":true,"age":28,"key":"fu"}
parse 對特殊字符串的解析
關於解析后的數據類型:
System.out.println(new JsonParser().parse("").getClass().getSimpleName());//【JsonNull】,注意不是字符串
" " -> 【JsonNull】,注意不是字符串
"null" -> 【JsonNull】,注意不是字符串
"\"\"" -> 【JsonPrimitive】,是空字符串【】
"\"null\"" -> 【JsonPrimitive】,是字符串【null】
"\"[null]\"" -> 【JsonPrimitive】,是字符串【[null]】
.parse("[]").getAsJsonArray().size();//【0】,長度為0的數組。此數組並不為null,所以解析后不必判空
.parse("[0]").getAsJsonArray().get(0).getClass().getSimpleName();//【JsonPrimitive】,有一個元素的數組
"[null]" -> 【JsonNull】,有一個元素的數組
"[{}]" -> 【JsonObject】,有一個元素的數組
注意,所有的引號、大括號、中括號
都必須完整(這是一個合法的json字符串的基本要求),不然都會出現解析異常:
new JsonParser().parse("{");//End of input at line 1 column 2 path $.
new JsonParser().parse("{");//End of input at line 1 column 2 path $[0]
new JsonParser().parse("\"");//Unterminated string at line 1 column 2 path $
new JsonParser().parse("{key}");//Expected ':' at line 1 column 6 path $.key
new JsonParser().parse("1,2");//Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 3 path $
下面這些是可以的:
new JsonParser().parse("{key:value}");//這樣是可以的【{"key":"value"}】,所有的引號都可以去掉
new JsonParser().parse("{key:\"value\"}"));//這樣也是可以的【{"key":"value"}】
new JsonParser().parse("[0,null,\"\",{}]");
fromJson 對特殊字符串的處理
通過 Gson.fromJson 方法將一個json字符串解析為對象 AA 時,要求解析結果必須是一個JsonObject
,且要求必須保證解析后的每個字段的類型和 AA 中定義的一致:
new Gson().fromJson("[]", AA.class);//Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
System.out.println(new Gson().fromJson("1", AA.class));//Expected BEGIN_OBJECT but was NUMBER at line 1 column 2 path $
但是,其可以將解析后為 JsonNull 的結果轉換為 null 而不報異常:
String jString = null;
System.out.println(new Gson().fromJson(jString, AA.class));//null,注意是一個空對象,所以不能對此結果做任何操作,包括getClass
System.out.println(new Gson().fromJson(" ", AA.class));//和上述結果一樣為 null
所以,解析時為了防止解析異常,在不確定數據結構的情況下,可以做一些安全性判斷:
if (!TextUtils.isEmpty(response) && response.trim().startsWith("{")) {
//為了防止服務器返回數據格式錯誤導致解析異常
}
為某個字段提供多個屬性名
或者說將 json 字符串中的某一字段的值賦給要轉化的對象中的某一指定字段。比如,服務器可能通過不同的字段返回給我們錯誤信息,有的用tips、有的用msg、有的用errorinfo……現在,我們不管服務器用哪種方式返回給我們錯誤信息,我們都用 message
字段去接收。
@SerializedName("email_address")
public String emailAddress;
@SerializedName(value = "message", alternate = {"tips", "msg","errorinfo","error"})
public String message;
當上面的幾個屬性中出現任意一個時均可以得到正確的結果,如果多種屬性同時出,那么是以最后一個出現的值為准
。
用 JsonElement 去定義未知類型的字段
可以用Gson框架中的基類 【JsonElement】去定義json數據中不確定具體類型的某些字段。
如下面的 data ,對於不同的網絡請求,其內容是完全不一樣的,可能是一個JsonObject,也可能是一個JsonArray,也可能是一個String,那么就可以用一個 JsonElement 類型的字段去接收,Gson解析時不會解析具體內容,而是把此字段中的值【原封不動】的返回給你
。
PS:JsonElement 是 Gson 框架中所有元素的父類,其子類包括 JsonObject、JsonArray、JsonPrimitive、JsonNull。Json字符串中的所有 value 都可以被解析為 JsonElement 的子類。
public class Test {
public static void main(String[] args) {
String content1 = "{\"code\":\"100\",\"data\":{\"errorinfo\":\"成功\"}}";
String content2 = "{\"code\":\"100\",\"data\":\"成功\"}";
String content3 = "{\"code\":\"100\",\"data\":[\"成功\"]}";
System.out.println(new Gson().fromJson(content1, Response.class).data);
System.out.println(new Gson().fromJson(content2, Response.class).data);
System.out.println(new Gson().fromJson(content3, Response.class).data);
}
static class Response {
public String code;
public JsonElement data;//用一個JsonElement去接收不想解析的內容
}
}
打印內容為:
{"errorinfo":"成功"}
"成功"
["成功"]
- 拓展1,如果用 Object 去定義 data,則打印內容為:
{errorinfo=成功}
成功
[成功]
可以看到,兩者被解析后的內容是不一樣
的,用 JsonElement 定義時得到的內容是帶有雙引號的完整的原始結果
,而用 Object 去定義時得到的內容是被Gson框架處理后的結果
。
- 拓展2,如果用一個泛型 去定義 data,比如:
static class Response<T> {
public String code;
public T data;
}
測試結果和使用 Object 時完全一致。
- 拓展3,如果用 一個普通對象 AA 去定義 data,對於 content1,打印內容為:
AA@5910e440
。對於 content2、content3,直接異常!
解析 Json 字符串中的內容
注意,一定要保證 get 和 getAs* 方法獲取的 key 存在且格式兼容,否則會直接崩掉!
String jsonString = "{'flag':true,'data':{'name':'張三','age':18,'address':'廣州'}}";
JsonObject root = new JsonParser().parse(jsonString).getAsJsonObject();
boolean flag = root.getAsJsonPrimitive("flag").getAsBoolean();
String name = root.getAsJsonObject("data").get("name").getAsString();
System.out.println(flag + " " + name);//true 張三
格式化 Json 字符串
return new GsonBuilder()
.setPrettyPrinting()
.create()
.toJson(new JsonParser().parse(content));
將序列化后的內容寫到 控制台、文件、SB中
gson.toJson(user, System.out); //直接寫到控制台。System.out 繼承自 PrintStream 實現了Appendable接口
FileWriter fileWriter=new FileWriter("d://a.txt");//將toJson后的字符串拼接到文件中
gson.toJson(person, fileWriter);//FileWriter 繼承自 OutputStreamWriter 繼承自 Writer 實現了Appendable接口
fileWriter.close();
StringBuilder builder=new StringBuilder();//將toJson后的字符串拼接到StringBuilder中
gson.toJson(person, builder);//StrngBuilder、StringBuffer都是Appendable接口的實現類
System.out.println(builder.toString());
使用 JsonWriter 編寫 Json 串
JsonWriter writer = new JsonWriter(new OutputStreamWriter(System.out));//可以先寫到文件中,再從文件中讀取
writer.beginObject()
.name("name").value("包青天")
.name("sport").beginArray().value("籃球").value("排球").endArray()//數據
.name("info").beginObject().name("age").value(28).endObject()//對象
.endObject();
writer.close(); //{"name":"包青天","sport":["籃球","排球"],"info":{"age":28}}
寫到指定位置:
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);//可以先寫到文件中,再從文件中讀取
writer.setIndent(" ");//設置縮進格式,一般使用2-4個空格,或使用制表符\t
//...上面的內容
System.out.println(stringWriter.getBuffer().toString());
結果
{
"name": "包青天",
"sport": [
"籃球",
"排球"
],
"info": {
"age": 28
}
}
2018-5-27