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的章节中进行讲解,不过一般很少用这个注解。 注解讲解完。 ◆ ◆ 该系列的其他文章 : 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
