一.JSON概念部分
1.json概念
定义:JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。可用于数据标记,存储,传输。
2.json解析示意图
json的解析一般有三种方式,即Gson,Jackson,Fastjson
3.json语法格式
总体来说就是键值对
对象
举例:
{
"name":"ljh",
"age":21
}
如果是数组,就加个中括号[]
举例:array数组里面是两个对象
"array":[
{
"name":"ljh",
"age":21
},
{
"name":"ljh1",
"age":22
}
]
如果符合这些格式,就说明是格式良好的json,方便使用gson等来解析。
二.Gson
1.Gson相关的注解
①@SerializedName
:里面的参数作为json的key
class MyClass {
@SerializedName("name") //a => name
String a;
@SerializedName(value = "name1", alternate = {"name2", "name3"})
String b;
String c;
public MyClass(String a, String b, String c) {
this.a = a;
this.b = b;
this.c = c;
}
public static void test(){
MyClass target = new MyClass("v1", "v2", "v3");
Gson gson = new Gson();
String json = gson.toJson(target);
System.out.println(json);
/**
* {"name":"v1","name1":"v2","c":"v3"}
*/
}
}
②@Expose
:过滤掉是否需要序列化/反序列化的属性
class User0 {
@Expose
private String firstName;//默认参与序列化,也参与反序列化
@Expose(serialize = false)
private String lastName;//不参与序列化,但参与反序列化
@Expose(serialize = false, deserialize = true)
private String emailAddress; //不参与序列化,但参与反序列化(默认)
@Expose(serialize = true,deserialize = false)
private String password;//参与序列化,但不参与反序列化
public User0(String firstName, String lastName, String emailAddress, String password) {
this.firstName = firstName;
this.lastName = lastName;
this.emailAddress = emailAddress;
this.password = password;
}
@Override
public String toString() {
return "User0{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", emailAddress='" + emailAddress + '\'' +
", password='" + password + '\'' +
'}';
}
public static void test(){
/**
* 如果要让expose注解发挥作用,必须
* new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
* 这样来构建Gson对象
*/
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String json = gson.toJson(new User0("jinhao","liu","email","111"));
System.out.println(json);
/**
* {"firstName":"jinhao","password":"111"}
*/
User0 user0 = gson.fromJson(json,User0.class);
System.out.println(user0);
/**
* User0{firstName='jinhao', lastName='null', emailAddress='null', password='null'}
* 需要注意的是password参与序列化,所以json中可以打印password的值。但是他不参与
* 反序列化,所以解析的时候password赋默认值null
*/
}
}
可以看下@Expose
的源码。
可以发现他赋的默认值都为true
③@since
和@until
版本控制。当使用since
注解的时候,意为当版本大于等于since
注解的参数的时候,被since
注解修饰的属性才会参与序列化/反序列化,否则忽略
class User1 {
private String firstName;
private String lastName;
@Since(1.0) private String emailAddress; //当前版本>=1.0时才会参与序列化/反序列化,否则忽略
@Since(1.1) private String password;//当前版本>=1.0时才会参与序列化/反序列化,否则忽略
public User1(String firstName, String lastName, String emailAddress, String password) {
this.firstName = firstName;
this.lastName = lastName;
this.emailAddress = emailAddress;
this.password = password;
}
}
class User2 {
private String firstName;
private String lastName;
@Until(1.1) private String emailAddress;//当前版本<=1.1时参加序列化/反序列化
@Until(1.1) private String password;//当前版本<=1.1时参加序列化/反序列化
public User2(String firstName, String lastName, String emailAddress, String password) {
this.firstName = firstName;
this.lastName = lastName;
this.emailAddress = emailAddress;
this.password = password;
}
}
上面是两个类,下面是main
方法
//定义版本
Gson gson = new GsonBuilder()
.setVersion(1.2).create();
User1 user1 = new User1("jinhao","liu","email","111");
User2 user2 = new User2("jinhao","liu","email","111");
String json1 = gson.toJson(user1);
String json2 = gson.toJson(user2);
System.out.println("json1:"+json1);
System.out.println("json2:"+json2);
/**
* json1:{"firstName":"jinhao","lastName":"liu","emailAddress":"email","password":"111"}
* json2:{"firstName":"jinhao","lastName":"liu"}
*/
④@JsonAdapter
指定自定义TypeAdapter
(详细内容后面有)
@JsonAdapter(UserJsonAdapter.class)
class User {
public final String firstName, lastName;
User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return "User{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
上面是类,下面是自定义的JsonAdapter
class UserJsonAdapter extends TypeAdapter<User> {
@Override
public void write(JsonWriter out, User user) throws IOException {
out.beginObject();
out.name("name");
out.value(user.firstName + " " + user.lastName);
out.endObject();
}
@Override
public User read(JsonReader in) throws IOException {
in.beginObject();
in.nextName();
String[] nameParts = in.nextString().split(" ");
in.endObject();
return new User(nameParts[0], nameParts[1]);
}
}
下面是测试结果
User user = new User("gg","Zero");
String gsonStr = gson.toJson(user);
/**
* gsonStr:{"name":"gg Zero"}
* 如果不用自定义JsonAdapter,则结果为
* gsonStr:{"firstName":"gg","lastName":"Zero"}
*/
System.out.println("gsonStr:" + gsonStr);
user = gson.fromJson(gsonStr,User.class);
System.out.println("user: " + user);
/**
* user: User{firstName='gg', lastName='Zero'}
*/
2.Gson基本使用
import com.google.gson.Gson;
public class GsonTest {
//普通的类,重写了toString方法
static class GsonBean{
public int i;
public String str;
@Override
public String toString() {
return "GsonBean{" +
"i=" + i +
", str='" + str + '\'' +
'}';
}
}
//基本使用
public static void main(String ... args){
Gson gson = new Gson();
//转换成json
System.out.println( gson.toJson(1));
System.out.println( gson.toJson("zero"));
/**
* 1
* "zero"
*/
int[] values = {1,2,3};
System.out.println( gson.toJson(values));
/**
* [1,2,3]
*/
//解析json
int i = gson.fromJson("1",int.class);
System.out.println("i: " + i);
/**
* i: 1
*/
GsonBean gsonBean = new GsonBean();
gsonBean.i= 2;
gsonBean.str = "str";
String json = gson.toJson(gsonBean);
System.out.println("json: " + json);
/**
* json: {"i":2,"str":"str"}
*/
GsonBean gsonBean1 = gson.fromJson(json,GsonBean.class);
System.out.println("gsonBean: " + gsonBean1);
/**
* gsonBean: GsonBean{i=2, str='str'}
*/
}
}
3.自定义TypeAdapter
如果不指定TypeAdapter
,则一般是使用使用了反射的TypeAdapter
,也就是系统默认的。这种方式比较耗性能。而且有一些特殊情况无法处理。
①那么何时使用自定义TypeAdapter
呢?
(1)传回的字符串或者数组为null
,使用时若不加空指针判断,容易出现空指针异常。
(2)测试用的数值为0,结果用GsonFomat
生成的对象默认为int
类型,但可能该字段的真实类型为float
,所以之后收到类型为float
的数据时,就可能导致解析出错。
一般是为了解决上面两个问题才使用自定义TypeAdapter
的,也就是改变解析Json
的策略(read
方法),当然也可以改变转换成Json
时的格式(write
方法)
注意:一个Bean
类对应一个TypeAdapter
或者JsonDeserializer
②先看一个正常使用的自定义TypeAdapter
Foo
类
static class Foo {
private String s;
private int i;
public Foo() {
this(null, 5);
}
public Foo(String s, int i) {
this.s = s;
this.i = i;
}
}
test2
方法
public static void test2() {
//自定义TypeAdapter
Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class
, new TypeAdapter<Foo>() {
@Override
public void write(JsonWriter out, Foo value) throws IOException {
if (value == null) {//进行非空判断
out.nullValue();
return;
}
//把Food对象制定成你自己定义的格式的字符串进行输出:不一定是json格式了,就看你怎么组织
out.beginObject();
out.name("s").value(value.s+"111222");
out.name("i").value(value.i);
out.endObject();
}
@Override
public Foo read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {//进行非空判断
in.nextNull();
return null;
}
//读取json串并封装成Foo对象返回之
final Foo foo = new Foo();
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "s":
foo.s = in.nextString();
break;
case "i":
foo.i = in.nextInt();
break;
}
}
in.endObject();
return foo;
}
}).create();
Foo foo = new Foo();
String json = gson.toJson(foo);
System.out.println(json);
/**
*{"s":"null111222","i":5}
*/
}
也可以用下面这个方法来取代
public static void test3() {
//nullSafe()演示
Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class
, new TypeAdapter<Foo>() {
@Override
public void write(JsonWriter out, Foo value) throws IOException {
out.beginObject();
out.name("s").value(value.s);
out.name("i").value(value.i);
out.endObject();
}
@Override
public Foo read(JsonReader in) throws IOException {
//读取json串并封装成Foo对象返回之
final Foo foo = new Foo();
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "s":
foo.s = in.nextString();
break;
case "i":
foo.i = in.nextInt();
break;
}
}
in.endObject();
return foo;
}
}.nullSafe()).create();
Foo foo = new Foo();
String json = gson.toJson(foo);
System.out.println(json);
}
不同点在于上面的test2
是可以自己自定义处理null
,但是test3
是调用了nullSafe
方法来让系统帮忙处理
③再来看另外一种防止null
的方式——自定义JsonDeserializer
首先看正常情况
public static void test1() {
//TODO:
String json = "{\n" +
" \"name\": \"java\",\n" +
" \"authors\": [\n" +
" {\n" +
" \"id\": \"1'\",\n" +
" \"name\": \"Joshua Bloch'\"\n" +
" },\n" +
" {\n" +
" \"id\": \"2'\",\n" +
" \"name\": \"Tom\"\n" +
" }\n" +
" ]\n" +
"}";
Gson gson = new Gson();
GsonError1 gsonError1 = gson.fromJson(json, GsonError1.class);
System.out.println("json:"+json);
System.out.println(gsonError1);
}
打印结果为
json:{
"name": "java",
"authors": [
{
"id": "1'",
"name": "Joshua Bloch'"
},
{
"id": "2'",
"name": "Tom"
}
]
}
GsonError1{name='java', authors=[AuthorsBean{id='1'', name='Joshua Bloch''}, AuthorsBean{id='2'', name='Tom'}]}
也就是说此时authors
是有值的,是正常的,不为null
。
再看当authors
为null
的时候
public static void test2() {
//TODO:
String json = "{\n" +
" \"name\": \"java\",\n" +
" \"authors\": \"\"\n" +
"}";
Gson gson = new Gson();
GsonError1 gsonError1 = gson.fromJson(json, GsonError1.class);
System.out.println(gsonError1);
}
结果会报错
这种情况就可以使用自定义TypeAdapter
来防止报错了,比如当authors
为null
的时候,我给他再赋值为null
就不会报错了,或者调用nullsafe
方法。打印出是
GsonError1{name='java', authors=null}
除了自定义TypeAdapter
,还可以自定义JsonDeserializer
public static void test3() {
//TODO:
String json = "{\n" +
" \"name\": \"java\",\n" +
" \"authors\": \"\"\n" +
"}";
GsonBuilder gsonBuilder = new GsonBuilder();
//注册TypeAdapter
gsonBuilder.registerTypeAdapter(GsonError1.class, new GsonError1Deserializer());
Gson gson = gsonBuilder.create();
GsonError1 gsonError1 = gson.fromJson(json, GsonError1.class);
System.out.println(gsonError1);
}
GsonError1Deserializer
类
static class GsonError1Deserializer implements JsonDeserializer<GsonError1> {
@Override
public GsonError1 deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
final JsonObject jsonObject = json.getAsJsonObject();
final JsonElement jsonTitle = jsonObject.get("name");
final String name = jsonTitle.getAsString();
JsonElement jsonAuthors = jsonObject.get("authors");
GsonError1 gsonError1 = new GsonError1();
if (jsonAuthors.isJsonArray()) {//如果数组类型,此种情况是我们需要的
//关于context在文章最后有简单说明
GsonError1.AuthorsBean[] authors = context.deserialize(jsonAuthors, GsonError1.AuthorsBean[].class);
gsonError1.setAuthors(Arrays.asList(authors));
} else {//此种情况为无效情况
gsonError1.setAuthors(null);
}
gsonError1.setName(name);
return gsonError1;
}
}
④面试题
如何解决?
自定义TypeAdapter
或自定义JsonDeserializer
或者调用nullSafe
方法