GSON-使用指南(三)

本次的主要内容:

  • 字段过滤的几种方法
    • 基于@Expose注解
    • 基于版本
    • 基于修饰符
    • 基于策略
  • POJO与JSON的字段映射规则

一、字段过滤的几种方法

字段过滤Gson中比较常用的技巧,在处理业务逻辑时可能需要在设置的POJO中加入一些字段,但显然在序列化的过程中是不需要的,在序列化的过程中可能带来一个问题就是循环引用 ,那么在用Gson序列化之前为了不防止这样的事情发生,你不得不作另外的处理。

以一个商品分类Category 为例:

{
  "id": 1,
  "name": "电脑",
  "children": [
    {
      "id": 100,
      "name": "笔记本"
    },
    {
      "id": 101,
      "name": "台式机"
    }
  ]
}

一个大分类,可以有很多小分类,那么显然我们在设计Category类时Category本身既可以是大分类,也可以是小分类。

public class Category {
    public int id;
    public String name;
    public List children;
}

但是为了处理业务,我们还需要在子分类中保存父分类,最终会变成下面的情况。

public class Category {
    public int id;
    public String name;
    public List children;
    //因业务需要增加,但并不需要序列化
    public Category parent; 
}

但是上面的parent字段是因业务需要增加的,那么在序列化时并不需要,所以在序列化时就必须将其排除,那么在Gson中如何排除符合条件的字段呢?下面提供4种方法,大家可根据需要自行选择合适的方式。

基于@Expose注解

@Expose提供了两个属性,且都有默认值,开发者可以根据需要设置不同的值。

GSON-使用指南(三)_第1张图片

@Expose 注解从名字上就可以看出是暴露的意思,所以该注解是用于对外暴露字段的。可是我们以前用Gson的时候也没@Expose 注解,还不是正确的序列化为JSON了么?是的,所以该注解在使用new Gson() 时是不会发生作用的。该注解必须和GsonBuilder配合使用。

使用方法: 简单来说,就是在需要导出的字段上加上@Expose 注解,不导出的字段不加。

@Expose //默认反序列化和序列化都为true
@Expose(deserialize = true,serialize = true) //反序列化和序列化都生效,等价于上一条
@Expose(deserialize = true,serialize = false) //反序列化时生效,序列化时不生效
@Expose(deserialize = false,serialize = true) //序列化时生效
@Expose(deserialize = false,serialize = false) //反序列化和序列化都不生效,和不写注解一样

注:根据上面的图片可以得出,所有值为true的属性都是可以不写的(默认值是true)。

通过上面的例子加以说明:

public class Category {
    @Expose
    public int id;
    @Expose
    public String name;
    @Expose
    public List children;//子类别列表

    //不需要序列化,所以不加 @Expose 注解,等价于 @Expose(deserialize = false,serialize = false)
    public Category parent;//保存父类别

    public Category() {
    }

    public Category(int id, String name, List children, Category parent) {
        this.id = id;
        this.name = name;
        this.children = children;
        this.parent = parent;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List getChildren() {
        return children;
    }

    public void setChildren(List children) {
        this.children = children;
    }

    public Category getParent() {
        return parent;
    }

    public void setParent(Category parent) {
        this.parent = parent;
    }
    public Category(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

在使用Gson时也不能只是简单的new Gson()了。

public static void main(String[] args) throws Exception {
        Gson gson = new GsonBuilder()//基本配置
//                .excludeFieldsWithoutExposeAnnotation()//个性化配置-排除没有暴露注解的字段
                .create();//完成配置
        Category category=new Category();
        category.setId(1);
        category.setName("ktz-dg");
        category.setChildren(new ArrayList(){
            {
                add(new Category(11,"ktz-dg-child1"));
                add(new Category(12,"ktz-dg-child2"));
            }
        });
        System.out.println(gson.toJson(category));//{"id":1,"name":"ktz-dg","children":[{"id":11,"name":"ktz-dg-child1"},{"id":12,"name":"ktz-dg-child2"}]}
    }

基于版本

Gson在对基于版本的字段导出提供了两个注解 @Since 和 @Until,也需要和GsonBuilder.setVersion(Double)配合使用。@Since 和 @Until都接收一个Double值。

GSON-使用指南(三)_第2张图片

使用方法:当前版本(GsonBuilder中设置的版本) 大于等于Since的值时该字段导出,小于Until的值时该字段导出。

public class SinceUntilSample {
    @Since(4) //导出条件:>=4
    public String since;
    @Until(5) //导出条件:<5
    public String until;

    public SinceUntilSample() {
    }

    public SinceUntilSample(String since, String until) {
        this.since = since;
        this.until = until;
    }

    public String getSince() {
        return since;
    }

    public void setSince(String since) {
        this.since = since;
    }

    public String getUntil() {
        return until;
    }

    public void setUntil(String until) {
        this.until = until;
    }
}

测试:

public static 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));
}
public static void main(String[] args) throws Exception{
    sineUtilTest(3);//{"until":"until"}
    sineUtilTest(4);//{"since":"since","until":"until"}
    sineUtilTest(5);//{"since":"since"}
}

注:当一个字段被两个注解同时注解时,需同时满足两个条件。

基于修饰符

什么是修饰符? public、static 、final、private、protected 这些就是。

常见的访问修饰符有public/private/protected/default(概念上的),所以这种方式是比较特殊的。

使用方式:

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三种类型的字段。

public static void main(String[] args) throws Exception{
    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"}
}

基于策略(自定义规则)

其实用得最多的还是自定义规则,好处是功能强大、灵活,缺点是相比其它3种方法稍微麻烦些。基于策略是利用Gson提供的ExclusionStrategy接口,同样需要使用GsonBuilder,相关API有2个,分别是addSerializationExclusionStrategy 和addDeserializationExclusionStrategy,分别针对序列化和反序化。这里以序列化为例。

例如:

public 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";
}
public static void main(String[] args) throws Exception{
    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);//排除int或Integer类型的字段
                    }
                })
            .create();//标配+个性化配置完成
    System.out.println(gson.toJson(new ModifierSample()));//默认情况下,GSON从序列化/反序列化过程中会排除瞬态和静态字段
    //{"publicField":"public","protectedField":"protected","defaultField":"default","privateField":"private"}
}

二、 POJO与JSON的字段映射规则

既然叫映射规则那么说的自然是有规律的情况。

还是之前User的例子,已经去除所有注解:

User user = new User("怪盗kidou", 24);
user.emailAddress = "[email protected]";

GsonBuilder提供了FieldNamingStrategy接口,setFieldNamingPolicy()和setFieldNamingStrategy()两个方法。

默认实现

GsonBuilder.setFieldNamingPolicy 方法与Gson提供的一个枚举类FieldNamingPolicy配合使用,该枚举类提供了5种实现方式分别为:

GSON-使用指南(三)_第3张图片

自定义实现

GsonBuilder.setFieldNamingStrategy 方法需要与Gson提供的FieldNamingStrategy接口配合使用,用于实现POJO的字段与JSON的字段相对应。上面的FieldNamingPolicy实际上也实现了FieldNamingStrategy接口,也就是说FieldNamingPolicy也可以使用setFieldNamingStrategy的方法。

用法:

public class User {
    String name;
    int age;
    String email;
    Date birth;
    String emailAddress;

    public User(String name, int age, String email, Date birth, String emailAddress) {
        this.name = name;
        this.age = age;
        this.email = email;
        this.birth = birth;
        this.emailAddress = emailAddress;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = 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;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public User(String name, int age, String email, Date birth) {
        this.name = name;
        this.age = age;
        this.email = email;
        this.birth = birth;
    }
}
public static void main(String[] args) throws Exception{
    User user = new User("ktz-dg", 22);
    user.emailAddress = "[email protected]";
    Gson gson = new GsonBuilder()
            .setFieldNamingStrategy(new FieldNamingStrategy() {//定制字段名称策略
                @Override
                public String translateName(Field f) {
                    //自定义映射规则,返回解析的字段名称
                    if ("name".equals(f.getName())) {
                        return "user-name";
                    }
                    return f.getName();//返回原字段名
                }
            })
            .create();
    System.out.println(gson.toJson(user));//{"user-name":"ktz-dg","age":22,"emailAddress":"[email protected]"}
}

注意: @SerializedName注解拥有最高优先级,在加有@SerializedName注解的字段上FieldNamingStrategy不生效!

你可能感兴趣的:(GSON-使用指南(三))