天天看點

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