1. 基本使用方法[fromJson/toJson]
将字符串解析成相应的对象 or 将对象Json化 这个应该是最常用的吧
Gson gson = new Gson();
String result = gson.fromJson("str",String.class);
int result1 = gson.fromJson("100",int.class);
Double result2 = gson.fromJson("210.34",Double.class);
Person person = new Person();
result = gson.toJson(person)
2.属性名不一致情况 使用@SerializedName
这个问题主要是三端(server/前端/客户端)命名习惯不一致造成的,举个例子:
// 客户端 java:
private String userName = "zhangsan";
//前端 javascript:
let user_name = "zhangsan"
//后端 PHP:
$username = "zhangsan";
那么相应的json为:
"{'userName':'zhangsan'}"
"{'user_name':'zhangsan'}"
"{'username':'zhangsan'}"
最为客户端,需要兼容这些模式时,需要使用@SerializedName:
@SerializedName(value = "userName", alternate = {"user_name","username"})
public String userName;
其中value
是当前默认解析字段,没有的话,查找有没有user_name
或者username
,有的话,将对应的值映射到该字段上。相当于alernate
关键字是备选字段。
3. 使用GsonBuilder输出null
我们定义一个对象Student
:
public class Student {
private String name ;
private int age ;
//getter.setter方法
}
默认情况下,null字段是不输出的:
Student s = new Student();
String str = gson.toJson(s);
System.out.println(str)
结果为:
{"age":0}
如果需要输入name,那么需要定义GsonBuilder:
//自定义GsonBuilder
Gson gson = new GsonBuilder().serializeNulls().create();
gson.toJson(s, System.out);
输出为:
{"name":null,"age":0}
4.输出格式化Gson
Gson gson2 = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
输出:
{
"name": null,
"age": 0
}
5.设置时间格式:
Gson gson2 = new GsonBuilder().setDateFormat("yyyy-MM-dd").create();
gson2.toJson(new java.util.Date(), System.out);
输出为:
"2018-07-09"
6.生成不可以用的Json字符串[貌似没什么卵用]
Gson gson2 = new GsonBuilder().setDateFormat("yyyy-MM-dd").generateNonExecutableJson().create();
gson2.toJson(new java.util.Date(), System.out);
输出为:
)]}'
"2018-07-09"
7.禁止Html字符串转义:
String html = "<p> hello world</p>" ;
Gson gson2 = new GsonBuilder().disableHtmlEscaping().create();
gson2.toJson(html,System.out);
禁止字符转义之后,变为:
"<p> hello world</p>"
未禁止转义之后:
"\u003cp\u003e hello world\u003c/p\u003e" //UTF-8转义
8.字段过滤
主要的原因是安全因素,人为添加字段,有时候序列化&反序列化需要过滤这些字段:
8.1 @Expose字段:
Expose中文名称是曝光的意思,就是你添加了,它就会序列化/反序列化了,没有的话就算了。
Student类中添加”address”字段:
public class Student {
private String name ;
private int age ;
@Expose(serialize = true, deserialize = true)
private String address;
}
那么toGson为:
Student student = new Student("tom",12,"sh");
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
gson.toJson(student,System.out);
输出为:
{"address":"sh"}
当然@Expost注解上还有serialize
和deserialize
关键字,按字面意思在序列化和反序列化时,要不要将该字段接入操作。
8.2 基于版本,基于注解@Until和@Since
@Until
是 <= 某Version
@Since
是 >= 某Version
@Since(18)
private int age ;
设置当前版本:
Student student = new Student("tom",12,"sh");
Gson gson = new GsonBuilder().setVersion(17).create();
gson.toJson(student,System.out);
当前设置版本17 < @Since(18)此时,此时将不会输出age:
{"name":"tom","address":"sh"}
更改Version:
Gson gson = new GsonBuilder().setVersion(20).create();
当前设置版本20 > @Since(18)此时,此时输出为:
{"name":"tom","age":12,"address":"sh"}
同理,@Until也是类似的,将不再做类比了。
当然对于同一个字段,你可以同时使用@Since和@Until
@Since(20)
@Until(5)
private String address;
8.3 基于访问修饰符
设置相应的修饰符对象字段:
public class ModifierTest {
private String privateName = "privateName";
public String publicName = "publicName";
public final String finalName = "finalName";
public final static String finalStaticName = "finalStaticName";
}
使用Modifier字段过滤:
ModifierTest test = new ModifierTest();
//去除private修饰符字段
Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.PRIVATE).create();
gson.toJson(test,System.out);
输出为:
{"publicName":"publicName","finalName":"finalName","finalStaticName":"finalStaticName"}
去掉final 和 public字段:
Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.FINAL, Modifier.PUBLIC).create();
输出为:
{"privateName":"privateName"}
8.4 自定义规则
直接使用Gson中提供的ExclusionStrategy
接口:
Student student = new Student("tom",12,"sh");
Gson gson = new GsonBuilder().addSerializationExclusionStrategy(new ExclusionStrategy() {
//跳过的field将不参与序列化/反序列化
@Override
public boolean shouldSkipField(FieldAttributes f) {
//name 直接跳过
if("name".equals(f.getName())) return true;
Expose expose = f.getAnnotation(Expose.class);
if(expose != null && !expose.deserialize()) return true ; //按照Expose注解 跳过
return false;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
//直接排除某些特定的类
return clazz == int.class || clazz == Integer.class;
}
}).create();
gson.toJson(student, System.out);
输出为:
{"address":"sh"}
9. 自定义TypeAdapter
很多时候,我们都会遇到这种情况,我们定义age字段是int型的,但是服务器给我们返回的是""
字段,如果不做处理,使用Gson解析就会抛出NumberFormatException,所以为了解决这个容错性,你不可能把后端拉出来打一顿,所以只有另想它法。
幸好Gson提供了TypeAdapter抽象类,可以抽象解析我们需要的数据,来个简单一点的,直接解析注册Int.class解析器:
String personUrl = "{\"name\" : \'zhangsan\' , \'age\' : '' , \'address\' : \'sh\'}" ;
Gson gson = new GsonBuilder().registerTypeAdapter(int.class, new TypeAdapter<Integer>() {
@Override
public void write(JsonWriter out, Integer value) throws IOException {
out.value(String.valueOf(value));
}
@Override
public Integer read(JsonReader in) throws IOException {
String value = in.nextString();
//System.out.println("------>>" + value);
try {
return Integer.parseInt(value);
}catch (NumberFormatException ignore){
return -1;
}
}
}).create() ;
Person person = gson.fromJson(personUrl, Person.class);
System.out.println(person);
解析之后,此时的空字符串被解析成了-1:
Person{name='zhangsan', age=-1, address='sh'}
如果需要解析Student呢:
同样一个道理:
public class StudentTypeAdapter extends TypeAdapter<Student> {
@Override
public void write(JsonWriter out, User value) throws IOException {
out.beginObject();
out.name("name").value(out.name);
out.name("age").value(out.age);
out.name("address").value(out.address);
out.endObject();
}
@Override
public Student read(JsonReader in) throws IOException {
Student student = new Student();
in.beginObject();
while(in.hasNext()) {
switch(in.nextName()) {
case "name":
student.name = in.nextString();
break;
case "age" :
student.age = int.nextInt();
break;
case "address":
student.address = int.nextString();
break ;
}
}
in.endObject();
}
}
10. Gson与混淆
当代码需要混淆时,有gson参与时,需要注意以下几点:
1. 需要序列化的类,不能被混淆;
2. 使用泛型参与解析的基类BaseEntity需要实现java.io.Serializable接口。