从一个Bug开始,重新认识一个强大的 Gson

从一个 Bug 说起


不知道你们发现没有,你写完的程序无论当时怎么测试,过一段时间总会出 Bug 。再说一个每天都在发生的例子:在你写完一篇博客后,立即检查的话,总是查不出自己写的错别字。


据说这些都包含有一些大脑对当下事物处理的逻辑在里边,而且还有心理学因素在里边,我也不懂。反正现在就是程序出 Bug 了!出 Bug 了!!Bug !!!


事情是这样的,客户端请求服务器开放接口时,我们是以 JSON 字符串的形式返回的,这时候本来正常返回形式是这样:


{
"name": "mafly",
"age": "18",
"sex": "男"
}


但这个名字叫Mafly的人他不想让你知道他是男是女。就没填性别,程序中默认赋值为 null了,JSON 序列化时就发生了意想不到的事儿。


重现一下那个 Bug


用于转换Java对象为 Json 字符串和把 Json 字符串转换为对象的工具类有很多,Gson、Jackson、FastJson等,我们用的是 Gson, 它是 Google 开发的,还据传说它的性能比较强大一些。嗯,对,我们用的就是这个。先看示例代码:


public static void main(String[] args) {

HashMap<String, Object> map = new HashMap<String, Object>();
map.put("name", "mafly");
map.put("age", "18");
map.put("sex", null);

String jsonString = new Gson().toJson(map);
System.err.println(jsonString);
}


你猜会输出上面那段我们预期的 Json 串吗?然而并不会。


640?wx_fmt=jpeg


就是这个当 Sex=null时,用 Gson 的tojson方法会把 null 值忽略,从而序列化之后输出不包含这个属性值的 Json 串。其实我们的预期是输出 {"sex": ""} 或 {"sex": null}, 那怎么做呢?


通过搜索引擎发现,网上大概存在以下三种解决方案:


  • 调用 toJson(Object src, Type typeOfSrc, JsonWriter writer) 方法

  • 注册自定义 TypeAdapter

  • 设置 serializeNulls 属性值(推荐)


前两种方式我没测试,推荐第三种方式,简单方便,源码如下:


GsonBuilder gsonBuilder = new GsonBuilder();
String jsonString1 = gsonBuilder.serializeNulls().create().toJson(map);
System.err.println(jsonString1);


输出符合我们的需求了, {"sex": null},如图:


640?wx_fmt=jpeg


简单转到源码看了看,除了这一个设置还有很多,真是太方便了。调用serializeNulls()后, Gson 设置 serializeNulls 属性值为 true,默认是false,源码:


public GsonBuilder serializeNulls() {
this.serializeNulls = true;
return this;
}


重新认识一下 Gson


我看了几行 Gson 的源码后,我发现它可配置及特性多的数不胜数,可以用@SerializedName注解给属性重命名,用@Expose注解标识属性不进行序列化,支持 Map 的 key 为复杂对象的形式,日期类型转化为特定格式,还有区分版本进行显示,这些在日常项目中都极常用到,只不过好像我们之前都是自己实现的。


我简单建立了一个 UserInfo 实体类,里边包含了username、age、sex这三个属性值。试着测试了一下其中一两个:


1.@Expose 想让谁输出谁输出,注解代码如图:


640?wx_fmt=jpeg


设置excludeFieldsWithoutExposeAnnotation(),看下面第三行是输出:


640?wx_fmt=jpeg


2.@SerializedName 想怎么显示怎么显示,注解代码如图:


640?wx_fmt=jpeg


我想把age再 Json序列化时显示maflyAge,现在就可以了,输出如下图:


640?wx_fmt=jpeg


3.@Since、@Until 不同版本不同数据,注解代码如图:


640?wx_fmt=jpeg


这里要设置setVersion(3.2),@Since标注的在 3.2 版本或之后才会输出,@Until标注的只在 3.2 版本前才有。效果输出如下图:


640?wx_fmt=jpeg


当然,还有好多特性呢,比如字段首字母大写阿、结果格式化阿等等,我这里抛个砖,剩下的有需要的可以试试。


总结一下


这是由于一个 Json 序列化输出后发现的小 Bug ,也可以说不能算 Bug ,因为是我们对于自己使用的工具类并不了解导致的,所以,去试着了解你使用的任何一个开源工具类或项目,对于遇到的问题可以很好的得到解决,从而也可以提升自己吧。


精彩回顾

 程序员究竟能干多少年?

 互联网公司各岗位真实工作内容起底

 一次尴尬的采访和程序员的传奇脑洞!

 天一冷,程序员都穿上格子衫

 史上最真实的行业鄙视链曝光

 IT公司老板落水,各部门员工怎么救

 宿命之战:程序员VS产品经理

 作为一个前端,可以如何机智地弄坏一台电脑?

 程序员跟产品经理打起来了,这是一个需求引发的血案...

 后端说,你个前端不会用 headers吧,我怒了!

 有个厉害的程序员女朋友是什么体验?

 多年来,程序员经常加班的真相终于揭开了…




640?wx_fmt=gif




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用GSON解析JSON的通用代码示例: ```java import java.lang.reflect.Type; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; public class JsonUtils { private static Gson gson = new Gson(); /** * 将JSON字符串转换为对象 * @param json JSON字符串 * @param clazz 对象类型 * @return 对象 */ public static <T> T fromJson(String json, Class<T> clazz) { return gson.fromJson(json, clazz); } /** * 将JSON字符串转换为对象列表 * @param json JSON字符串 * @param type 对象类型 * @return 对象列表 */ public static <T> List<T> fromJsonList(String json, Type type) { return gson.fromJson(json, type); } /** * 将对象转换为JSON字符串 * @param object 对象 * @return JSON字符串 */ public static String toJson(Object object) { return gson.toJson(object); } /** * 将对象列表转换为JSON字符串 * @param list 对象列表 * @return JSON字符串 */ public static String toJson(List<?> list) { return gson.toJson(list); } /** * 将对象转换为JSON字符串,并指定排除字段 * @param object 对象 * @param excludeFields 排除字段 * @return JSON字符串 */ public static String toJsonExcludeFields(Object object, String... excludeFields) { Gson gson = new GsonBuilder().setExclusionStrategies(new ExcludeFieldsStrategy(excludeFields)).create(); return gson.toJson(object); } /** * 将对象列表转换为JSON字符串,并指定排除字段 * @param list 对象列表 * @param excludeFields 排除字段 * @return JSON字符串 */ public static String toJsonExcludeFields(List<?> list, String... excludeFields) { Gson gson = new GsonBuilder().setExclusionStrategies(new ExcludeFieldsStrategy(excludeFields)).create(); return gson.toJson(list); } /** * 排除字段策略 */ private static class ExcludeFieldsStrategy implements ExclusionStrategy { private String[] excludeFields; public ExcludeFieldsStrategy(String... excludeFields) { this.excludeFields = excludeFields; } @Override public boolean shouldSkipField(FieldAttributes f) { for (String excludeField : excludeFields) { if (f.getName().equals(excludeField)) { return true; } } return false; } @Override public boolean shouldSkipClass(Class<?> clazz) { return false; } } } ``` 使用示例: ```java // JSON字符串 String json = "{\"name\":\"张三\",\"age\":18}"; // 将JSON字符串转换为对象 Person person = JsonUtils.fromJson(json, Person.class); System.out.println(person.getName() + "," + person.getAge()); // 对象列表的JSON字符串 String jsonList = "[{\"name\":\"张三\",\"age\":18},{\"name\":\"李四\",\"age\":20}]"; // 将JSON字符串转换为对象列表 Type type = new TypeToken<List<Person>>() {}.getType(); List<Person> personList = JsonUtils.fromJsonList(jsonList, type); for (Person p : personList) { System.out.println(p.getName() + "," + p.getAge()); } // 将对象转换为JSON字符串 Person person = new Person("张三", 18); String json = JsonUtils.toJson(person); System.out.println(json); // 将对象列表转换为JSON字符串 List<Person> personList = Arrays.asList(new Person("张三", 18), new Person("李四", 20)); String jsonList = JsonUtils.toJson(personList); System.out.println(jsonList); // 将对象转换为JSON字符串,并指定排除字段 Person person = new Person("张三", 18); String json = JsonUtils.toJsonExcludeFields(person, "name"); System.out.println(json); // 将对象列表转换为JSON字符串,并指定排除字段 List<Person> personList = Arrays.asList(new Person("张三", 18), new Person("李四", 20)); String jsonList = JsonUtils.toJsonExcludeFields(personList, "name"); System.out.println(jsonList); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值