天天看点

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

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

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

一、前言

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

二、Gson的注解

注:在Gson中有5类注解  。

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

  ◆ ◆ ◆ 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":"[email protected]","title":"engineer"}

               

实际

{"id":"100",name":"chunlynn","email_address":"[email protected]","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":"[email protected]","title":"engineer"}

          
          
           
           
            {"id":"100",name":"chunlynn","email_address":"[email protected]","title":"engineer"}
{"id":"100",name":"chunlynn","email":"[email protected]","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\":\"[email protected]\"}
          
          
                   {\"id\":\"100\",\"name\":\"chunlynn\",\"email_address\":\"[email protected]\"}
          
          
                   {\"id\":\"100\",\"name\":\"chunlynn\",\"email\":\"[email protected]\"}
          
          
                   */
          
          
                   String json1 = "{\"id\":\"100\",\"name\":\"chunlynn\",\"email_address\":\"[email protected]\"}";
          
          
                   String json2 = "{\"id\":\"100\",\"name\":\"chunlynn\",\"emailAddress\":\"[email protected]\",\"email_address\":\"[email protected]\",\"email\":\"[email protected]\"}";
          

          
                   Employee employee1 = gson.fromJson(json1, Employee.class);
          
          
                   System.out.println("email_address字段反序列化 ===> " + employee1);
          
          
                   //email_address字段反序列化 ===> Employee [id=100, name=chunlynn, [email protected]]
          

          
          
                   Employee employee2 = gson.fromJson(json2, Employee.class);
          
          
                   System.out.println("多种格式字段反序列化 ===> " + employee2);
          
          
                   // 多种格式字段反序列化 ===> Employee [id=100, name=chunlynn, [email protected]]
          

          
          
               }
          
          }

               

结果反序列化完全成功。这个注解在我们处理调用的远程接口返回的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", "[email protected]");
       
       
                String jsonString = gson.toJson(empyee);
       
       
                System.out.println("使用注解后的序列化==" + jsonString);
       
       
                // 使用注解后的序列化=={"id":"1001","name":"jeffchen","email":"[email protected]"}
       

       
       
                Employee employee3 = gson.fromJson(jsonString, Employee.class);
       
       
                System.out.println("使用注解后的反序列化==" + employee3);
       
       
                // 使用注解后的反序列化==Employee [id=1001, name=jeffchen, emailAddress[email protected]]
       
       

       
            }
       
       }

       
            

◆ ◆ ◆ 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", "[email protected]", new Date(), "engineer");
     
     
              String jsonString = gson.toJson(employee2);
     
     
              System.out.println("使用了@Expose注解后的序列化输出 ===》 " + jsonString);
     
     
              //使用了@Expose注解后的序列化输出 ===》 {"name":"chunlynn","email":"[email protected]","title":"engineer"}
     

     
              Employee2 retEmployee = gson.fromJson(jsonString, Employee2.class);
     
     
              System.out.println("使用了@Expose注解后的反序列化解析==》" + retEmployee);
     
     
              //使用了@Expose注解后的反序列化解析==》Employee2 [id=null, name=chunlynn, [email protected], birthday=null, title=engineer]
     

     
              String jsonString2 = "{\"id\":\"1007\",\"name\":\"jeffchen\",\"email\":\"[email protected]\",\"title\":\"boss\"}";
     
     
              Employee2 reEmployee2 = gson.fromJson(jsonString2, Employee2.class);
     
     
              System.out.println("使用了@Expose注解后的反序列化解析2==》" + reEmployee2);
     
     
              // 使用了@Expose注解后的反序列化解析2==》Employee2 [id=null, name=jeffchen, [email protected], 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", "[email protected]", new Date(), "engineer");
      

      
               /**
      
      
                * 序列化
      
      
                */
      
      
               String jsonStr = gson.toJson(employee3);
      
      
               System.out.println("使用@Expose()注解后的序列化输出 ==》 " + jsonStr);
      
      
               //使用了@Expose注解后的序列化输出 ===》 
      
      
               //{"name":"chunlynn","email":"[email protected]","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, [email protected], birthday=null, title=null]
      

      
               /**
      
      
                * 反序列化[2]
      
      
                */
      
      
               String jsonString2 = "{\"id\":\"1007\",\"name\":\"jeffchen\",\"email\":\"[email protected]\",\"title\":\"boss\"}";
      
      
               Employee3 reEmployee2 = gson.fromJson(jsonString2, Employee3.class);
      
      
               System.out.println("使用了@Expose()注解后的反序列化解析2==》" + reEmployee2);
      
      
               // 使用了@Expose()注解后的反序列化解析2==》
      
      
               //Employee3 [id=null, name=jeffchen, [email protected], 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", "[email protected]", new Date(), "engineer", "10086",
        
        
                         "羽毛球");
        

        
                 /**
        
        
                  * 序列化
        
        
                  */
        
        
                 String jsonStr = gson.toJson(employee);
        
        
                 System.out.println("使用@Since()和@Until()注解后的序列化输出 ==》 " + jsonStr);
        
        
                 //使用@Since()和@Until()注解后的序列化输出 ==》 
        
        
                 //{"name":"chunlynn","email":"[email protected]","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, [email protected], birthday=null, title=null, phoneNum=10086, habbit=null]
        

        
                 /**
        
        
                  * 反序列化[4]
        
        
                  */
        
        
                 String jsonString2 = "{\"id\":\"1007\",\"name\":\"jeffchen\",\"email\":\"[email protected]\",\"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, [email protected], birthday=null, title=boss, phoneNum=10010, habbit=null]
        
        
        
             }
        
        }
             

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

注解讲解完。

◆ ◆ 该系列的其他文章 : JSON解析类库之Gson(1) --- 简单JavaBean对象、带泛型的Bean对象与JSON互转

JSON解析类库之Gson(2) --- 泛型对象Map、List与JSON字符串互转

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

JSON解析类库之Gson(4)--- TypeAdapter接管序列化与反序列化(上)

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