JSON解析类库之Gson(3) --- Gson注解


---Gson类库学习, 生成与解析json数据,json字符串与Java对象互转


一、前言

Gson注解给我们的使用带来很多方便,特别是Java实体类字段与获得的JSON字符串的字段不一一对应时,注解发挥巨大作用,同时也简化了代码的开发。

二、Gson的注解

注:在Gson中有5类注解  。

 
◆ 1   @SerializedName注解(JSON字段重命名)
----------------------------------------------------------------------------------------------------
该注解能指定该字段在JSON中对应的字段名称,就是将POJO中的字段与JSON字符串中的字段对应起来。
输出的json使用另外一个名字,默认转换出来的json中和对象的字段是一样的,当然也 可以设置成不同,使用SerializedName 注解 。

作用1:转换关键字key,json转换成JavaBean时,json字段的key 默认必须和我们声明类的字段名称一样,当服务器端返回了关键字怎么办,比如key 为new switch这样,我们在声明类的时候不能写这样的字段,可能你想服务器端改动,他可能要改数据库,但是我告诉你,做服务端的大部分不愿意改动他的json,是很自私的!这时候重命名注解都排上用场了 。  第二种场景:服务器端返回的json 的key 简直太丑,或者太长,你想简化,my_parent_name,可以简化成mpn 。

从前面的POJO的生成与解析可以看出,json的字段和值是和pojo的名称和类型是一一对应的,但也有一定容错机制(如第一篇文章中,基本类型转换中,第3行将字符串的99.99转成double型,你可别告诉我都是字符串啊),但有时候也会出现一些不和谐的情况,如:

期望的json格式
         
         
{"id":"100",name":"chunlynn","emailAddress":"chunlynn@example.com","title":"engineer"}
实际
          
          
{"id":"100",name":"chunlynn","email_address":"chunlynn@example.com","title":"engineer"}

如果我们是通过http调用其他系统的接口而获得到的JSON字符串数据,显然JSON字符串的结果我们是无法改变的,而JSON的字段和我们的POJO中字段并没有一一对应,这样解析肯定会出错。这些以下划线命名的方式,在使用PHP作为后台开发语言时是很常见的情况,php和js在命名时一般采用下划线风格,而Java中一般采用的驼峰法,若要自己使用下划线风格时我会感到不适应,且不符合Java命名规范,怎么办?难到没有两全齐美的方法么?

Gson提供了一个 SerializedName的注解类,这应该就是我们要找的。

那么对于JSON中email_address这个字段对应POJO的字段则变成:
          
          
@SerializedName( "email_address" )
private String emailAddress;
这样的话,很好的保留了前端、后台、Java/Android各自的命名习惯。
注意: SerializedName中的value属性值,永远都是指JSON字符串中的字段值(POJO序列化后的值)。如 email_address指的就是JSON字符串中的字段值,通过注解@ SerializedName("email_address")将它和POJO中的字段值emailAddress关联映射起来。

你以为这样就完了么?

如果接口中设计不严谨,或者其它地方可以重用该类,其它字段都一样,就emailAddress 字段不一样,比如有下面三种情况那怎么?重新写一个?
          
          
{"id":"100",name":"chunlynn","emailAddress":"chunlynn@example.com","title":"engineer"}
{"id":"100",name":"chunlynn","email_address":"chunlynn@example.com","title":"engineer"} {"id":"100",name":"chunlynn","email":"chunlynn@example.com","title":"engineer"}

为POJO字段提供备选属性名
SerializedName注解提供了两个属性,上面用到了其中一个,别外还有一个属性alternate,接收一个String数组。
注:alternate需要2.4及以上版本。
alternate是反序列化时才有用。不管 SerializedName中的值是多少,反序列化后对应的POJO的字段值都是emailAddress。
          
          
@SerializedName(value   =   "emailAddress",   alternate   =   {   "email",   "email_address"   })
private String emailAddress;
如果JSON中有 email 就会解析成 emailAddress ,如果有 email_address 也会解析成 emailAddress.
注意1:value中的值不能出现在alternate中;
注意2:alternate的备选字段, 会后面的替换前面的。
当上面的三个属性(email_address、email、emailAddress)中都出现,或出现任意一个时均可以得到正确的结果。
:当多种情况同时出时,以最后一个出现的值为准。

情况1:
实体类:
          
          
import  com.google.gson.annotations.SerializedName;
public class Employee {
    private String id;
    private String name;
    @SerializedName(value = "emailAddress", alternate = { "email", "email_address" })
    private String emailAddress;
//为了代码简洁,这里移除了getter和setter方法、toString方法等
     
}

测试类【反序列化】:
         
         
package  com.chunlynn.gson;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GsonTest12 {
    public static void main(String[] args) {
        Gson gson = new GsonBuilder()//
                //.setPrettyPrinting()//格式化输出(序列化)
                .enableComplexMapKeySerialization() 支持Map的key为复杂对象的形式
                .create();
        /*
        {\"id\":\"100\",\"name\":\"chunlynn\",\"emailAddress\":\"chunlynn@example.com\"}
        {\"id\":\"100\",\"name\":\"chunlynn\",\"email_address\":\"chunlynn@example.com\"}
        {\"id\":\"100\",\"name\":\"chunlynn\",\"email\":\"chunlynn@example.com\"}
        */
        String json1 = "{\"id\":\"100\",\"name\":\"chunlynn\",\"email_address\":\"chunlynn@example.com\"}";
        String json2 = "{\"id\":\"100\",\"name\":\"chunlynn\",\"emailAddress\":\"chunlynn@example.com\",\"email_address\":\"chunlynn@example.com\",\"email\":\"chunlynn@example.com\"}";
        Employee employee1 = gson.fromJson(json1, Employee.class);
        System.out.println("email_address字段反序列化 ===> " + employee1);
        //email_address字段反序列化 ===> Employee [id=100, name=chunlynn, emailAddress=chunlynn@example.com]
        Employee employee2 = gson.fromJson(json2, Employee.class);
        System.out.println("多种格式字段反序列化 ===> " + employee2);
        // 多种格式字段反序列化 ===> Employee [id=100, name=chunlynn, emailAddress=chunlynn@example.com]
    }
}
结果反序列化完全成功。这个注解在我们处理调用的远程接口返回的JSON字段与我们自己定义的POJO的字段不匹配时非常有用。

情况2:
实体类:
      
      
public   class  Employee {
    private String id;
    private String name;
//序列化后就变成了email (json串中的字段名)      @SerializedName(value   =   "email",   alternate   =   {   "emailAddress",   "email_address"   })
    private String emailAddress;
    //为了代码简洁,这里移除了getter和setter方法、toString方法、构造方法等 }
测试类【序列化与反序列化】:
      
      
package  com.chunlynn.gson;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GsonTest13 {
    public static void main(String[] args) {
        Gson gson = new GsonBuilder()//
                //.setPrettyPrinting()//格式化输出(序列化)
                .enableComplexMapKeySerialization() 支持Map的key为复杂对象的形式
                .create();
        Employee empyee = new Employee("1001", "jeffchen", "jeff@126.com");
        String jsonString = gson.toJson(empyee);
        System.out.println("使用注解后的序列化==" + jsonString);
        // 使用注解后的序列化=={"id":"1001","name":"jeffchen","email":"jeff@126.com"}
        Employee employee3 = gson.fromJson(jsonString, Employee.class);
        System.out.println("使用注解后的反序列化==" + employee3);
        // 使用注解后的反序列化==Employee [id=1001, name=jeffchen, emailAddress=jeff@126.com]
    }
}


◆ 2   @Expose注解(字段过滤)
----------------------------------------------------------------------------------------------------
指定哪些是要暴露转换的属性。有时候我们不需要把实体的所有属性都导出,只想把一部分属性导出为Json,或只想对一部分POJO的字段进行反序列化。

源码:默认既可以序列化又可以反序列化。下面是Gson的 Expose 注解源码:

       
       
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Expose {
      public   boolean   serialize()   default   true;
   public boolean deserialize() default true;
}
Expose注解有两个属性,默认值都是true,即默认同时对序列化和反序列化都有效。

即使用了Expose默认注解后:标注了 Expose注解的字段才会被序列化输出,同时,反序列化时,标注了 Expose注解的字段才会被反序列化。

情况1:默认注解  @Expose ,序列化和反序列化都有效
实体类:
   
   
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public   class  Employee2 {
    private String id;
@Expose //等同于@Expose(deserialize = true,serialize = true)        private   String   name;
@ Expose
//序列化后就变成了email (json串中的字段名)
    @SerializedName(value = "email", alternate = { "emailAddress", "email_address" })
    private String emailAddress;
    private Date birthday;
     @ Expose
    private String title;
//为了代码简洁,这里移除了getter和setter方法、toString方法、构造方法等
   
}


测试类:
注意使用了@Expose注解,则必须在GsonBuilder类实例化时进行设置。只有配置了 .excludeFieldsWithoutExposeAnnotation() 时,@Expose才会起作用。

    
    
package  com.chunlynn.gson;
import java.util.Date;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GsonTest14 {
    public static void main(String[] args) {
        Gson gson = new GsonBuilder()//
                //.setPrettyPrinting()//格式化输出(序列化)
                .excludeFieldsWithoutExposeAnnotation() // 不导出实体中没有用@Expose注解的属性。
                .setDateFormat("yyyy-MM-dd HH:mm:ss") //序列化时间转化为特定格式  
                .create();
        Employee2 employee2 = new Employee2("1002", "chunlynn", "chunlynn@163.com", new Date(), "engineer");
        String jsonString = gson.toJson(employee2);
        System.out.println("使用了@Expose注解后的序列化输出 ===》 " + jsonString);
        //使用了@Expose注解后的序列化输出 ===》 {"name":"chunlynn","email":"chunlynn@163.com","title":"engineer"}
        Employee2 retEmployee = gson.fromJson(jsonString, Employee2.class);
        System.out.println("使用了@Expose注解后的反序列化解析==》" + retEmployee);
        //使用了@Expose注解后的反序列化解析==》Employee2 [id=null, name=chunlynn, emailAddress=chunlynn@163.com, birthday=null, title=engineer]
        String jsonString2 = "{\"id\":\"1007\",\"name\":\"jeffchen\",\"email\":\"jeff@163.com\",\"title\":\"boss\"}";
        Employee2 reEmployee2 = gson.fromJson(jsonString2, Employee2.class);
        System.out.println("使用了@Expose注解后的反序列化解析2==》" + reEmployee2);
        // 使用了@Expose注解后的反序列化解析2==》Employee2 [id=null, name=jeffchen, emailAddress=jeff@163.com, birthday=null, title=boss]
    }
}

上面的实体类中,我们使用的是@Expose的默认属性,默认情况下对序列化和反序列化都有效。

Expose注解类两个属性:serialize和deserialize可以分别设置序列化和反序列化。 

分为以下几种情况:
1:不添加@Expose注解等同于@Expose(deserialize = false,serialize = false) 不做任何解析。
2:@Expose(deserialize = true,serialize = false) 只解析时用,也就是反序列化可以,序列化不可以
3:@Expose(deserialize = false,serialize = true) 序列化可以,反序列化不行
4:@Expose(deserialize = true,serialize = true) 既可以序列化,也可以反序列化,等同于默认。

下面实例,将分别演示这四种情况。

情况2:分别设置序列化与反序列化暴露字段
实体类:
     
     
public   class  Employee3 {
    private String id;
    @Expose //等同于 @Expose(deserialize = true,serialize = true)  
    private String name;
    @Expose
    @SerializedName(value = "email", alternate = { "emailAddress", "email_address" })
    private String emailAddress;
    @Expose(serialize = true, deserialize = false)
    private Date birthday;
@Expose(serialize = false, deserialize = true)      private   String   title;
//为了代码简洁,这里移除了getter和setter方法、toString方法、构造方法等
}

测试类:

     
     
package  com.chunlynn.gson;
import java.util.Date;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GsonTest15 {
    public static void main(String[] args) {
        Gson gson = new GsonBuilder()//
                //.setPrettyPrinting()//格式化输出(序列化)
                .excludeFieldsWithoutExposeAnnotation() // 不导出实体中没有用@Expose注解的属性 
                .setDateFormat("yyyy-MM-dd HH:mm:ss") //序列化时间转化为特定格式  
                .create();
        Employee3 employee3 = new Employee3("1005", "chunlynn", "chunlynn@163.com", new Date(), "engineer");
        /**
         * 序列化
         */
        String jsonStr = gson.toJson(employee3);
        System.out.println("使用@Expose()注解后的序列化输出 ==》 " + jsonStr);
        //使用了@Expose注解后的序列化输出 ===》 
//{"name":"chunlynn","email":"chunlynn@163.com","birthday":"2017-05-05 11:20:36"}
        /**
         * 反序列化[1]
         */
        Employee3 retEmployee = gson.fromJson(jsonStr, Employee3.class);
        System.out.println("使用了@Expose()注解后的反序列化解析==》" + retEmployee);
        //使用了@Expose()注解后的反序列化解析==》
//Employee3 [id=null, name=chunlynn, emailAddress=chunlynn@163.com, birthday=null, title=null]
        /**
         * 反序列化[2]
         */
        String jsonString2 = "{\"id\":\"1007\",\"name\":\"jeffchen\",\"email\":\"jeff@163.com\",\"title\":\"boss\"}";
        Employee3 reEmployee2 = gson.fromJson(jsonString2, Employee3.class);
        System.out.println("使用了@Expose()注解后的反序列化解析2==》" + reEmployee2);
        // 使用了@Expose()注解后的反序列化解析2==》
//Employee3 [id=null, name=jeffchen, emailAddress=jeff@163.com, birthday=null, title=boss]
    }
}

◆ 3、4   @Since(double v) 与 @Until(double v)注解 (版本控制)
----------------------------------------------------------------------------------------------------
有时候我们的实体类会随着版本的升级而修改。一些新的字段是后续加进来的,在新的版本软件中才使用。
@Since(double v) 与 @Until(double v)注解源码:
         
         
@Retention (RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Since {
  /**
   * the value indicating a version number since this member
   * or type has been present.
   */
  double value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Until {
  /**
   * the value indicating a version number until this member
   * or type should be ignored.
   */
  double value();
}
@Since  和  @Until 都接收一个 Double 值。
Since(4.0),表示当前版本大于等于4.0时才有效。当前版本是指在GsonBuilder.setVersion(double v)中设置的版本。
Until(4.0),表示当前版本小于4.0时才有效。

实体类:
        
        
public   class  Employee4 {
    private String id;
    @Expose
    //等同于 @Expose(deserialize = true,serialize = true)  
    private String name;
    @Expose
    @SerializedName(value = "email", alternate = { "emailAddress", "email_address" })
    private String emailAddress;
    @Expose(serialize = true, deserialize = false)
    private Date birthday;
    @Expose(serialize = false, deserialize = true)
    private String title;
@Expose
    @Since(4.0) //表示该字段从4.0开始生效
    private String phoneNum;
@Expose
    @Until(2.0) //表示2.0及其以后该字段就失效了
    private String habbit;
  //为了代码简洁,这里移除了getter和setter方法、toString方法、构造方法等
}

测试类:
注意使用了@Since(double v)、@Until(double v)注解,则必须在GsonBuilder类实例化时进行设置。只有配置了 .setVersion(double v) 时,@Since(double v)、@Until(double v)才会起作用。

       
       
package  com.chunlynn.gson;
import java.util.Date;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GsonTest16 {
    public static void main(String[] args) {
        Gson gson = new GsonBuilder()//
                //.setPrettyPrinting()//格式化输出(序列化)
                .excludeFieldsWithoutExposeAnnotation() // 不导出实体中没有用@Expose注解的属性 
                .setDateFormat("yyyy-MM-dd HH:mm:ss") //序列化时间转化为特定格式  
                .setVersion(5.0) //设置当前版本号
                .create();
        Employee4 employee = new Employee4("1005", "chunlynn", "chunlynn@163.com", new Date(), "engineer", "10086",
                "羽毛球");
        /**
         * 序列化
         */
        String jsonStr = gson.toJson(employee);
        System.out.println("使用@Since()和@Until()注解后的序列化输出 ==》 " + jsonStr);
        //使用@Since()和@Until()注解后的序列化输出 ==》 
        //{"name":"chunlynn","email":"chunlynn@163.com","birthday":"2017-05-05 11:52:40","phoneNum":"10086"}
        /**
         * 反序列化[3]
         */
        Employee4 retEmployee = gson.fromJson(jsonStr, Employee4.class);
        System.out.println("使用@Since()和@Until()注解后的反序列化解析==》" + retEmployee);
        //使用@Since()和@Until()注解后的反序列化解析==》
        //Employee4 [id=null, name=chunlynn, emailAddress=chunlynn@163.com, birthday=null, title=null, phoneNum=10086, habbit=null]
        /**
         * 反序列化[4]
         */
        String jsonString2 = "{\"id\":\"1007\",\"name\":\"jeffchen\",\"email\":\"jeff@163.com\",\"title\":\"boss\",\"phoneNum\":\"10010\",\"habbit\":\"游泳\"}";
        Employee4 reEmployee2 = gson.fromJson(jsonString2, Employee4.class);
        System.out.println("使用@Since()和@Until()注解后的反序列化解析2==》" + reEmployee2);
        //使用@Since()和@Until()注解后的反序列化解析2==》
        //Employee4 [id=null, name=jeffchen, emailAddress=jeff@163.com, birthday=null, title=boss, phoneNum=10010, habbit=null]
    }
}

◆ 5   JsonAdapter 注解 (使用TypeAdapter时的注解)
----------------------------------------------------------------------------------------------------
该注解在TypeAdapter的章节中进行讲解,不过一般很少用这个注解。

注解讲解完。

该系列的其他文章

---------------------------------------------------------------------------------------------------
版权声明:本文为博主(chunlynn)原创文章,转载请注明出处 :http://blog.csdn.net/chenchunlin526/article/details/71173404


GitHub 加速计划 / js / json
41.72 K
6.61 K
下载
适用于现代 C++ 的 JSON。
最近提交(Master分支:1 个月前 )
960b763e 4 个月前
8c391e04 6 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐