Gson学习-3
原文地址:https://www.jianshu.com/p/e740196225a4
主要内容
1.字段过滤的几种方法
2.POJO类与JSON的字段映射规则
一、字段过滤的几种方法
以一个商品分类Category为例。
{
"id": 1,
"name": "电脑",
"children": [
{
"id": 100,
"name": "笔记本"
},
{
"id": 101,
"name": "台式机"
}
]
}
一个大分类,可以有很多小分类,那么显然我们在设计Category类时Category本身既可以是大分类,也可以是小分类。
public class Category {
public int id;
public String name;
public List<Category> children;
}
但是为了处理业务,我们还需要在子分类中保存父分类,最终会变成下面的情况
public class Category {
public int id;
public String name;
public List<Category> children;
//因业务需要增加,但并不需要序列化
public Category parent;
}
但是上面的parent字段是因业务需要增加的,那么在序列化是并不需要,所以在序列化时就必须将其排除,那么在Gson中如何排除符合条件的字段呢?
①基于@Expose注解
使用方法: 简单说来就是需要导出的字段上加上@Expose 注解,不导出的字段不加。注意是不导出的不加。
@Expose //默认序列化和反序列化都都生效
@Expose(deserialize = true,serialize = true) //序列化和反序列化都都生效,等价于上一条
@Expose(deserialize = true,serialize = false) //反序列化时生效
@Expose(deserialize = false,serialize = true) //序列化时生效
@Expose(deserialize = false,serialize = false) // 和不写注解一样
上面的例子变成下面这样
public class Category {
@Expose public int id;
@Expose public String name;
@Expose public List<Category> children;
//不需要序列化,所以不加 @Expose 注解,
//等价于 @Expose(deserialize = false,serialize = false)
public Category parent;
}
使用Gson时注意配置。
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
gson.toJson(category);
配置Gson以排除所有字段,使其不考虑没有Expose批注的序列化或反序列化。
完整例子
public class User {
@Expose
public String name;
@Expose
public int age;
public String email;
//省略其他
public User(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
主函数
String str = "{\"name\":\"星星\",\"age\":22,\"email\":\"5456s45@qq.com\"}";
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
User user = gson.fromJson(str,User.class);
System.out.println(user.toString());
//User{name='星星', age=22, email='null'}
②基于版本
Gson在对基于版本的字段导出提供了两个注解 @Since和 @Until,和GsonBuilder.setVersion(Double)配合使用。@Since 和@Until都接收一个Double值。
使用方法:当前版本(GsonBuilder中设置的版本) 大于等于Since的值时该字段导出,小于Until的值时该该字段导出。
class SinceUntilSample {
@Since(4)//大于等于4有效
public String since;
@Until(5)//小于5有效
public String until;
}
public void sineUtilTest(double version){
SinceUntilSample sinceUntilSample = new SinceUntilSample();
sinceUntilSample.since = "since";
sinceUntilSample.until = "until";
Gson gson = new GsonBuilder().setVersion(version).create();
System.out.println(gson.toJson(sinceUntilSample));
}
//当version <4时,结果:{"until":"until"}
//当version >=4 && version <5时,结果:{"since":"since","until":"until"}
//当version >=5时,结果:{"since":"since"}
注:当一个字段被@Since @Until俩个同时注解时,需两者同时满足条件。
③基于访问修饰符
什么是修饰符? public、static 、final、private、protected这些就是,所以这种方式也是比较特殊的。
class ModifierSample {
final String finalField = "final";
static String staticField = "static";
public String publicField = "public";
protected String protectedField = "protected";
String defaultField = "default";
private String privateField = "private";
}
使用GsonBuilder.excludeFieldsWithModifiers构建gson,支持int的可变参数,值由java.lang.reflect.Modifier提供,下面的程序排除了private 、 final 和static 类型。
ModifierSample modifierSample = new ModifierSample();
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PRIVATE)
.create();
System.out.println(gson.toJson(modifierSample));
// 结果:{"publicField":"public","protectedField":"protected","defaultField":"default"}
④基于策略(自定义规则)
基于策略是利用Gson提供的ExclusionStrategy接口,同样需要使用GsonBuilder,相关API 2个,分别是addSerializationExclusionStrategy和addDeserializationExclusionStrategy 分别针对序列化和反序化时。这里以序列化为例。
Gson gson = new GsonBuilder()
.addSerializationExclusionStrategy(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
// 这里作判断,决定要不要排除该字段,return true为排除
if ("finalField".equals(f.getName())) return true; //按字段名排除
Expose expose = f.getAnnotation(Expose.class);
if (expose != null && expose.deserialize() == false) return true; //按注解排除
return false;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
// 直接排除某个类 ,return true为排除
return (clazz == int.class || clazz == Integer.class);
}
})
.create();
强大啊!
完整例子反序列化
String str = "{\"name\":\"星星\",\"age\":22,\"email\":\"5456s45@qq.com\"}";
Gson gson = new GsonBuilder()
.addDeserializationExclusionStrategy(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
//按字段排除return true为排除
if("email".equals(fieldAttributes.getName())){
return true;
}
return false;
}
@Override
public boolean shouldSkipClass(Class<?> aClass) {
//按雷系排除return true为排除
if(aClass==int.class){
return true;
}
return false;
}
})
.create();
User user = gson.fromJson(str,User.class);
System.out.println(user.toString());
//User{name='星星', age=0, email='null'}
二、 POJO类与JSON的字段映射规则
前面介绍了@SerializedName这个注解的使用
这里学习setFieldNamingPolicy 采用默认类的策略和setFieldNamingStrategy 采用自定义的策略的使用
默认策略有
| IDENTITY | 在Gson中使用此命名策略将确保字段名称保持不变。 |
|---|---|
| LOWER_CASE_WITH_DASHES | 在Gson中使用此命名策略会将Java字段名称从其camel cased表单修改为小写字段名称,其中每个单词用短划线( - )分隔。 |
| LOWER_CASE_WITH_DOTS | 在Gson中使用此命名策略会将Java字段名称从其camel cased表单修改为小写字段名称,其中每个单词用点(.)分隔。 |
| LOWER_CASE_WITH_UNDERSCORES | 在Gson中使用此命名策略会将Java字段名称从其camel cased表单修改为小写字段名称,其中每个单词由下划线(_)分隔。 |
| UPPER_CAMEL_CASE | 在Gson中使用此命名策略将确保Java字段名称的第一个“字母”在序列化为其JSON格式时大写。 |
| UPPER_CAMEL_CASE_WITH_SPACES | 在Gson中使用此命名策略将确保Java字段名称的第一个“字母”在序列化为其JSON格式时大写,并且单词将用空格分隔。 |
举例子
[IDENTITY] : 返回本身的格式
user ---> user
userName ---> userNmae
[UPPER_CAMEL_CASE] -- 首个字母大写
someFieldName ---> SomeFieldName
_someFieldName ---> _SomeFieldName
[UPPER_CAMEL_CASE_WITH_SPACES] -- 以空格隔开
someFieldName ---> Some Field Name
_someFieldName ---> _Some Field Name
[LOWER_CASE_WITH_UNDERSCORES] -- 以下划线隔开
someFieldName ---> some_field_name
_someFieldName ---> _some_field_name
aStringField ---> a_string_field
aURL ---> a_u_r_l
[LOWER_CASE_WITH_DASHES] -- 以中间线隔开
someFieldName ---> some-field-name
_someFieldName ---> _some-field-name
aStringField ---> a-string-field
aURL ---> a-u-r-l
[LOWER_CASE_WITH_DOTS] ---- 以点号隔开
someFieldName ---> some.field.name
_someFieldName ---> _some.field.name
aStringField ---> a.string.field
aURL ---> a.u.r.l
public class Model {
public String userName;
public String emailAddress;
public Model() {
}
public Model(String userName, String emailAddress) {
this.userName = userName;
this.emailAddress = emailAddress;
}
}
Model m = new Model("星星","4564wsa@qq.com");
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(LOWER_CASE_WITH_DASHES)
.create();
System.out.println(gson.toJson(m));
//{"user-name":"星星","email-address":"4564wsa@qq.com"}
String mstr = "{\"user-name\":\"星星\",\"email-address\":\"4564wsa@qq.com\"}";
//IDEA先打双引号,然后复制字符串到里面,自动转义 nice
m = gson.fromJson(mstr,Model.class);
System.out.println(m.toString());
//Model{userName='星星', emailAddress='4564wsa@qq.com'}
自定义策略
Gson gson = new GsonBuilder()
.setFieldNamingStrategy(new FieldNamingStrategy() {
@Override
public String translateName(Field f) {
//实现自己的规则
return null;
}
})
.create();
Gson gson = new GsonBuilder()
.setFieldNamingStrategy(new FieldNamingStrategy() {
@Override
public String translateName(Field field) {
if(field.getName().equals("name")){
return "NAME";
}
return field.getName();
}
})
.create();
User user = new User("星星",22,"465464q@qq.com");
gson.toJson(user,System.out);//{"NAME":"星星","age":22,"email":"465464q@qq.com"}
注意: 前面介绍的@SerializedName注解拥有最高优先级,在加有@SerializedName注解的字段上FieldNamingStrategy不生效!