Json基础知识
Json是什么
Json是JavaScript Object Notation(JavaScript对象表示法)的缩写,是一种轻量级的文本数据交换格式,是用来存储和交换文本信息的语法。
它独立于语言且具有自我描述性,更容易理解。虽然使用Javascript语法来描述数据对象,但很多编程语言都支持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语法的子集:
数据在名称/值对{ key:value }中
数据由逗号分隔
大括号保存对象
中括号保存数组
Json的key为字符串,值可以是:
数字(整数或浮点数)
字符串(在双引号中)
逻辑值(true 或 false)
数组(在中括号中)
对象(在大括号中)
null
Json对象
JSON 对象在大括号({ })中书写,对象可以包含多个名称/值对。名称/值对中使用冒号(:)分割,每个名称/值对对使用逗号(,)分割。
一个典型的例子:
{"id": "31624984467"}
Json数组
JSON 数组在中括号中书写,数组可包含多个对象,也可只是合法的基本数据类型:
[{ "id": "31624984467"},{ "owner": "155140314@N03"}]
["a","b","c"]
Android中的Json解析
利用Android自带的Json解析类
使用JSONObject,JSONArray来解析:
JSONObject类:生成Json对象,可以完成Json字符串与Java对象的相互转换。
JSONArray类:生成Json数组,可以完成Json字符串与Java集合或对象的相互转换。
JSONStringer类:Json文本构建类,这个类可以帮助快速和便捷的创建JSON text, 每个JSONStringer实体只能对应创建一个JSON text。
JSONException类:Json异常。
使用步骤介绍:
获取Json字符串。一般是从网络获取,为了方便这里直接从本地获取。
使用JSONObject类,利用Json字符串生成JSONObject对象。
使用getJSONObject,getJSONArray等方法一个个的将Json字符串分解,再从中抠出所需要的属性。
举个栗子:
josn数据为:
{ "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数据
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 items, JSONObject jsonBody) throws JSONException {//先获取最外层的对象photos
JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
//再获取photos内的对象数组photo
JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
for (int i = 0; i < 3; i++) {
//再依据具体的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文档的解析,使用深度优先遍历,寻找到需要的标记然后消耗掉它。对象的解析从beginObject()开始,到endObject()结束。数组的解析从beginArray()开始,到endArray()结束。
下面我们来看一看吧,json数据依旧是之前的!
JsonReader reader = new JsonReader(new StringReader(json));//获取reader对象
try {
reader.beginObject();//从Json流中消耗一个标记,开始解析对象
while (reader.hasNext()) {//循环判断当前数组或者对象是否有下一个元素
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 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 items) throws IOException {
reader.beginArray();//开始数组解析
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();//结束数组解析
}`
注意:
如果使用了nextName(),而没有使用next **()( **可以是String,Int,Long等)方法或者skipValue(),这时候就会触发异常,这一组方法需要配套使用。同理使用了beginObject()就要使用endObject()结尾,使用了beginArray()就要使用endArray()结尾。
开源库Gson使用
尝试完了繁琐的Android自带的Json解析类,现在就来看看第三方开源库Gson的威力吧!
导入依赖
dependencies { implementation 'com.google.code.gson:gson:2.8.5' }
Gson的基本使用
Gson对象的创建
有两种方式来创建Gson对象。
第一种是直接new一个对象: Gson gson = new Gson();
第二种是利用GsonBuilder来创建,可以进行多项配置:Gson gson = new GsonBuilder().create();
Gson的反序列化
Gson最为重要的两个方法分别是toJson()和fromJson()。toJson()用来序列化,fromJson()用来反序列化。
1 简单Json数组解析:
Json数据:
[ "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数组转化为字符串数组:
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数组转化为字符串List:
Gson gson = new Gson();
String json_t2 = null;
try {
json_t1 = jsonFromLocal(R.raw.json_t2);
} catch (IOException e) {
e.printStackTrace();
}
List list = gson.fromJson(json_t2,new TypeToken>(){}.getType());
for (String st : list) {
Log.i(TAG, st);
}
Gson中的泛型表示:
在解析Json数组时,一般使用两种方法,一个是数组,一个是List。而因为List对于增删操作比较方便,一般选用List。
但对于List来说,上面代码中的 String[].class ,不能直接改为List.class。因为对于Java来说 List 和List这两个字节码文件只有一个,就是 List.class,这就是Java泛型使用时要注意的泛型擦除问题。
为了解决上述问题,Gson提供TypeToken来实现对泛型的支持。TypeToken 这个类帮助我们捕获(capture)像 List 这样的泛型信息。Java编译器会把捕获到的泛型信息编译到这个匿名内部类里,然后在运行时就可以被getType()方法用反射的 API 提取到。也就是将泛型T转成 .class
2 稍复杂Json数据解析:
Json数据,目的是为了拿到totol数据:
{
"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"
}
]
}
第一步:根据Json数据建立简单Java类。可以使用Android Studio平台中的GsonFormat插件来快速构建。
public class JsonT1 {
public List 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数据。这里有两种方式,第一种是利用JsonParse类解析,有点类似于Android自带的JsonObject和JsonArray解析方式,得要扣需要的key。下面来看一下实现代码。
JsonObject jsonObject = new JsonParser().parse(json_t1).getAsJsonObject();
JsonArray jsonArray = jsonObject.getAsJsonArray("photo");
List jsonT1List = new ArrayList<>();
for (JsonElement element : jsonArray) {
JsonT1.PhotoBean jsonT1 = gson.fromJson(element, new TypeToken() {
}.getType());
jsonT1List.add(jsonT1);
}
Log.i(TAG, jsonT1List.get(0).total);
第二种是直接将Json数据解析为类对象,在得到对象之后再从对象内取出需要的List。
JsonT1 t1 = gson.fromJson(json_t1, JsonT1.class);
List photoBeanList = t1.photo;
Log.i(TAG,photoBeanList.get(0).total);
比较而言第二种方式更简洁一点,而且遇到更复杂一点的Json数据也可以使用。
3复杂Json数据解析
当遇到这种Json数据时:
{
"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大显身手的地方了。使用这种流式处理方式再复杂的数据也能搞定。JsonReader是不是有点眼熟?没错,它和之前提到的Android自带的JsonReader处理方式几乎没有区别,连名字都一样。
JsonReader.png
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的子类来接管目标类的序列化和反序列化过程,并且实现write和read方法。
public class SelfTypeAdapter extends TypeAdapter {
@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();