Json基礎知識
-
Json是什么
Json是JavaScript Object Notation(JavaScript對象表示法)的縮寫,是一種輕量級的文本數(shù)據(jù)交換格式,是用來存儲和交換文本信息的語法。
它獨立于語言且具有自我描述性,更容易理解。雖然使用Javascript語法來描述數(shù)據(jù)對象,但很多編程語言都支持Json。
Json的文件類型是".json",MIME類型是"application/json"。
-
Json的語法
典型的Json實例:
{ "photos": { "page": 1, "pages": 10, "perpage": 100, "total": "1000",
"photo": [
{ "id": "31624984467"}, {"owner": "155140314@N03"}, {"secret": "efa8606dc9"}]}}
Json語法是JS語法的子集:
- 數(shù)據(jù)在名稱/值對{ key:value }中
- 數(shù)據(jù)由逗號分隔
- 大括號保存對象
- 中括號保存數(shù)組
Json的key為字符串,值可以是:
- 數(shù)字(整數(shù)或浮點數(shù))
- 字符串(在雙引號中)
- 邏輯值(true 或 false)
- 數(shù)組(在中括號中)
- 對象(在大括號中)
- null
-
Json對象
JSON 對象在大括號({ })中書寫,對象可以包含多個名稱/值對。名稱/值對中使用冒號(:)分割,每個名稱/值對對使用逗號(,)分割。
一個典型的例子:
{"id": "31624984467"}
-
Json數(shù)組
JSON 數(shù)組在中括號中書寫,數(shù)組可包含多個對象,也可只是合法的基本數(shù)據(jù)類型:
[{ "id": "31624984467"},{ "owner": "155140314@N03"}]
["a","b","c"]
Android中的Json解析
利用Android自帶的Json解析類
-
使用JSONObject,JSONArray來解析:
JSONObject類:生成Json對象,可以完成Json字符串與Java對象的相互轉換。
JSONArray類:生成Json數(shù)組,可以完成Json字符串與Java集合或對象的相互轉換。
JSONStringer類:Json文本構建類,這個類可以幫助快速和便捷的創(chuàng)建JSON text, 每個JSONStringer實體只能對應創(chuàng)建一個JSON text。
JSONException類:Json異常。
使用步驟介紹:
- 獲取Json字符串。一般是從網(wǎng)絡獲取,為了方便這里直接從本地獲取。
- 使用JSONObject類,利用Json字符串生成JSONObject對象。
- 使用getJSONObject,getJSONArray等方法一個個的將Json字符串分解,再從中摳出所需要的屬性。
舉個栗子:
josn數(shù)據(jù)為:
{ "photos": { "page": 1, "pages": 10, "perpage": 100, "total": "1000",
"photo": [
{ "id": "31624984467", "owner": "155140314@N03","title": "Happy New Year 2019 : Happy New Year Happy New Year 2019", "url_s": "https:\/\/farm5.staticflickr.com\/4852\/31624984467_efa8606dc9_m.jpg"},
{ "id": "31624992207", "owner": "163032290@N04","title": "","url_s": "https:\/\/farm5.staticflickr.com\/4844\/31624992207_a3196f29b6_m.jpg"},
{ "id": "31624994507", "owner": "146047562@N03","title": "swt33.c33.kr????????????:swt33??:swt33???????,????????,?????????,?????????,????????","url_s": "https:\/\/farm5.staticflickr.com\/4819\/31624994507_0a022a924c_m.jpg"}]}
從資源目錄中獲取json數(shù)據(jù)
private String jsonFromLocal() throws IOException {
InputStream inStr = getResources().openRawResource(R.raw.jsontest);
InputStreamReader inReader = new InputStreamReader(inStr);
BufferedReader bufferedReader = new BufferedReader(inReader);
StringBuilder builder = new StringBuilder(" ");
String str = null;
while ((str = bufferedReader.readLine()) != null) {
builder.append(str);
builder.append("\n");
}
inReader.close();
bufferedReader.close();
return builder.toString();
}
接著就開始扣需要的屬性了。
//生成JSONObject對象
String json = jsonFromLocal();
JSONObject jsonBody = new JSONObject(json);
parseItem(items, jsonBody);`
private void parseItem(List<Item> items, JSONObject jsonBody) throws JSONException {//先獲取最外層的對象photos
JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
//再獲取photos內(nèi)的對象數(shù)組photo
JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
for (int i = 0; i < 3; i++) {
//再依據(jù)具體的key來獲取需要的屬性
JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);
Item item = new Item();
item.setId(photoJsonObject.getString("id"));
item.setCaption(photoJsonObject.getString("title"));
item.setUrl(photoJsonObject.getString("url_s"));
item.setOwner(photoJsonObject.getString("owner"));
items.add(item);
}
}
item類:
public class Item {
private String id;
private String url;
private String owner;
private String caption;
......(get,set等方法)
-
使用JsonReader來解析
這種方式有點類似XML文檔的解析,使用深度優(yōu)先遍歷,尋找到需要的標記然后消耗掉它。對象的解析從beginObject()開始,到endObject()結束。數(shù)組的解析從beginArray()開始,到endArray()結束。
下面我們來看一看吧,json數(shù)據(jù)依舊是之前的!
JsonReader reader = new JsonReader(new StringReader(json));//獲取reader對象 try { reader.beginObject();//從Json流中消耗一個標記,開始解析對象 while (reader.hasNext()) {//循環(huán)判斷當前數(shù)組或者對象是否有下一個元素 String propertyName = reader.nextName();//獲取key值 if (propertyName.equals("photos")) { readPhotos(reader, items); } else { reader.skipValue();//跳過下一個不需要的元素 } } reader.endObject();//結束對象解析 } finally { reader.close(); } } private void readPhotos(JsonReader reader, List<Item> items) throws IOException { reader.beginObject(); while (reader.hasNext()) { String propertyName = reader.nextName(); if (propertyName.equals("photo")) { readPhoto(reader, items); } else { reader.skipValue(); } } reader.endObject(); } private void readPhoto(JsonReader reader, List<Item> items) throws IOException { reader.beginArray();//開始數(shù)組解析 String id = null,url_s = null,owner = null,title = null; while (reader.hasNext()) { PhotoItem item = new PhotoItem(); reader.beginObject(); while (reader.hasNext()) { String propertyName = reader.nextName(); switch (propertyName) { case "id": id = reader.nextString();//獲取"id"的值,返回String類型 break; case "url_s": url_s = reader.nextString(); break; case "owner": owner = reader.nextString(); break; case "title": title = reader.nextString(); break; default: reader.skipValue(); break; } } item.setId(id); item.setUrl(url_s); item.setOwner(owner); item.setCaption(title); items.add(item); reader.endObject(); } reader.endArray();//結束數(shù)組解析 }`
注意:
如果使用了nextName(),而沒有使用next **()( **可以是String,Int,Long等)方法或者skipValue(),這時候就會觸發(fā)異常,這一組方法需要配套使用。同理使用了beginObject()就要使用endObject()結尾,使用了beginArray()就要使用endArray()結尾。
開源庫Gson使用
嘗試完了繁瑣的Android自帶的Json解析類,現(xiàn)在就來看看第三方開源庫Gson的威力吧!
- 導入依賴
dependencies { implementation 'com.google.code.gson:gson:2.8.5' }
-
Gson的基本使用
Gson對象的創(chuàng)建
有兩種方式來創(chuàng)建Gson對象。
第一種是直接new一個對象:
Gson gson = new Gson();
第二種是利用GsonBuilder來創(chuàng)建,可以進行多項配置:
Gson gson = new GsonBuilder().create();
Gson的反序列化
Gson最為重要的兩個方法分別是toJson()和fromJson()。toJson()用來序列化,fromJson()用來反序列化。
1 簡單Json數(shù)組解析:
Json數(shù)據(jù):
[ "Happy New Year 2019 : Happy New Year Happy New Year 2019",
"Karma's black iron prison",
"Hong Kong gov't bid to redevelop Fanling course 'absurd' says golf alliance convener"
]
Json數(shù)組轉化為字符串數(shù)組:
Gson gson = new Gson();
String json_t2 = null;
try {
json_t2 = jsonFromLocal(R.raw.json_t2);
} catch (IOException e) {
e.printStackTrace();
}
String[] strings = gson.fromJson(json_t2, String[].class);
for (String st : list) {
Log.i(TAG, st);
}
Json數(shù)組轉化為字符串List:
Gson gson = new Gson();
String json_t2 = null;
try {
json_t1 = jsonFromLocal(R.raw.json_t2);
} catch (IOException e) {
e.printStackTrace();
}
List<String> list = gson.fromJson(json_t2,new TypeToken<List<String>>(){}.getType());
for (String st : list) {
Log.i(TAG, st);
}
Gson中的泛型表示:
在解析Json數(shù)組時,一般使用兩種方法,一個是數(shù)組,一個是List。而因為List對于增刪操作比較方便,一般選用List。
但對于List來說,上面代碼中的 String[].class
,不能直接改為List<String>.class
。因為對于Java來說 List<String>
和List<Item>
這兩個字節(jié)碼文件只有一個,就是 List.class
,這就是Java泛型使用時要注意的泛型擦除問題。
為了解決上述問題,Gson提供TypeToken
來實現(xiàn)對泛型的支持。TypeToken
這個類幫助我們捕獲(capture)像 List 這樣的泛型信息。Java編譯器會把捕獲到的泛型信息編譯到這個匿名內(nèi)部類里,然后在運行時就可以被getType()
方法用反射的 API 提取到。也就是將泛型T轉成 .class
2 稍復雜Json數(shù)據(jù)解析:
Json數(shù)據(jù),目的是為了拿到totol數(shù)據(jù):
{
"photo": [
{
"page": 1,
"pages": 10,
"perpage": 100,
"total": "1000"
},
{
"page": 2,
"pages": 20,
"perpage": 200,
"total": "1000"
},
{
"page": 3,
"pages": 30,
"perpage": 300,
"total": "1000"
}
]
}
第一步:根據(jù)Json數(shù)據(jù)建立簡單Java類。可以使用Android Studio平臺中的GsonFormat插件來快速構建。
public class JsonT1 {
public List<PhotoBean> photo;
public static class PhotoBean {
/**
* page : 1
* pages : 10
* perpage : 100
* total : 1000
*/
public int page;
public int pages;
public int perpage;
public String total;
}
}
第二步:解析Json數(shù)據(jù)。這里有兩種方式,第一種是利用JsonParse類解析,有點類似于Android自帶的JsonObject和JsonArray解析方式,得要扣需要的key。下面來看一下實現(xiàn)代碼。
JsonObject jsonObject = new JsonParser().parse(json_t1).getAsJsonObject();
JsonArray jsonArray = jsonObject.getAsJsonArray("photo");
List<JsonT1.PhotoBean> jsonT1List = new ArrayList<>();
for (JsonElement element : jsonArray) {
JsonT1.PhotoBean jsonT1 = gson.fromJson(element, new TypeToken<JsonT1.PhotoBean>() {
}.getType());
jsonT1List.add(jsonT1);
}
Log.i(TAG, jsonT1List.get(0).total);
第二種是直接將Json數(shù)據(jù)解析為類對象,在得到對象之后再從對象內(nèi)取出需要的List。
JsonT1 t1 = gson.fromJson(json_t1, JsonT1.class);
List<JsonT1.PhotoBean> photoBeanList = t1.photo;
Log.i(TAG,photoBeanList.get(0).total);
比較而言第二種方式更簡潔一點,而且遇到更復雜一點的Json數(shù)據(jù)也可以使用。
3復雜Json數(shù)據(jù)解析
當遇到這種Json數(shù)據(jù)時:
{
"photos": {
"page": 1,
"pages": 10,
"perpage": 100,
"total": "1000",
"photo": [
{
"id": "31624984467",
"owner": "155140314@N03",
"secret": "efa8606dc9",
"server": "4852",
"farm": 5,
"title": "Happy New Year 2019 : Happy New Year Happy New Year 2019",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0,
"url_s": "https:\/\/farm5.staticflickr.com\/4852\/31624984467_efa8606dc9_m.jpg",
"height_s": "240",
"width_s": "240"
},
{
"id": "31624992207",
"owner": "163032290@N04",
"secret": "a3196f29b6",
"server": "4844",
"farm": 5,
"title": "",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0,
"url_s": "https:\/\/farm5.staticflickr.com\/4844\/31624992207_a3196f29b6_m.jpg",
"height_s": "135",
"width_s": "240"
},
"stat": "ok"
}
就到了JsonReader大顯身手的地方了。使用這種流式處理方式再復雜的數(shù)據(jù)也能搞定。JsonReader是不是有點眼熟?沒錯,它和之前提到的Android自帶的JsonReader處理方式幾乎沒有區(qū)別,連名字都一樣。
-
Gson注解
Gson的常用注解有
@Expose
,@SerializedName
,@Since
和@Until
,@JsonAdapter
。1
@Expose
注解本注解用于指定某個屬性是否進行序列化或反序列化,搭配GsonBuilder使用,源碼如下。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Expose {
public boolean serialize() default true;
public boolean deserialize() default true;
}
包含兩個屬性serialize
,deserialize
。serialize
用于指定是否進行序列化,deserialize
用于指定是否進行反序列化,默認為true。
舉個栗子:
@Expose(serialize = false, deserialize = true)//可以反序列化,不可以序列化
public int page;
使用:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
2 @SerializedName
注解
重命名指定的屬性,源碼如下。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SerializedName {
String value();
}
使用:
@SerializedName("pagesTotal")
public int pages;
3 @Since
和@Until
注解
和@Expose
注解一樣,用來指定某一屬性是否可以序列化或反序列化。@Since
和 @Until
都包含一個 Double 屬性值,用于設置版本號。Since 的意思是“自……開始”,Until 的意思是“到……為止”。當版本( GsonBuilder 設置的版本) 大于或等于 Since 屬性值或小于 Until 屬性值時字段會進行序列化和反序列化操作,而沒有聲明的字段都會加入序列化和反序列操作。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Until {
double value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Since {
double value();
}
使用:
@Since(1.1)
public int perpage;
@Until(1.8)
public String total;
Gson gson = new GsonBuilder().setVersion(1.3).create();
4 @JsonAdapter
注解
自定義序列化和反序列化,可以修飾類和字段。
首先需要自定義一個TypeAdapter的子類來接管目標類的序列化和反序列化過程,并且實現(xiàn)write和read方法。
public class SelfTypeAdapter extends TypeAdapter<JsonT1> {
@Override
public void write(JsonWriter out, JsonT1 value) throws IOException {
}
@Override
public JsonT1 read(JsonReader in) throws IOException {
JsonT1 t1 = new JsonT1();
in.beginObject();
//.....解析操作
in.endObject();
return t1;
}
}
接著是注冊自定義的SelfTypeAdapter類:
Gson gs = new GsonBuilder().registerTypeAdapter(JsonT1.class, new SelfTypeAdapter()).create();
- 其他Gson配置
1 格式化輸出
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.create();
2 日期時間格式化輸出
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.setDateFormat("yyyy-MM-dd HH:mm:ss:SSS")//日期時間格式
3 Null值輸出
Gson gson = new GsonBuilder()
.serializeNulls()
.create();
參考:
(https://juejin.im/post/59e5663f51882546b15b92f0)
(http://www.lxweimin.com/p/0444693c2639)
(http://www.lxweimin.com/p/886f7b7fca7d)
(http://www.runoob.com/json/js-json-arrays.html)