Gson 简单使用姿势小结

Gson 简单使用姿势小结

关于 Json 序列化的框架可以说比较多了,比如 Spring 默认的 Jackson,国内互联网用的比较多的 FastJson,本文则主要介绍一下 Gson 的简单使用姿势,并不会涉及到不同的 json 框架的性能对比

本文主要内容来源于官方教程: https://github.com/google/gson/blob/master/UserGuide.md

1. 依赖导入

首先我们借助 maven 来引入依赖包,按照自己的实际情况选择一个版本(简单的使用姿势与具体的版本并没有太大的关联性)

<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>

2. 基本的序列化与反序列化

一般来讲,我们通过 Gson 对象来实现 Json 的序列化与反序列化,如下是几个简单的序列化与反序列化的 case

// Serialization
Gson gson = new Gson();
gson.toJson(1);            // ==> 1
gson.toJson("abcd");       // ==> "abcd"
gson.toJson(new Long(10)); // ==> 10
int[] values = { 1 };
gson.toJson(values);       // ==> [1]

// Deserialization
int one = gson.fromJson("1", int.class);
Integer one = gson.fromJson("1", Integer.class);
Long one = gson.fromJson("1", Long.class);
Boolean false = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class);

上面的 case 中,主要就是借助gson.toJson来输出 Json 字符串,借助gson.fromJson返序列化得到对象

3. 对象序列化

对象的序列化与反序列化可以说是最常见的,在 Gson 的使用过程中,推荐的对象写法

  • filed:private 修饰
  • 不希望序列化的成员,添加transient修饰符
  • 默认无参构造方法(可选,这里跟人推荐保留,不同于 FastJson 的必须存在)
public static class BaseBean {
    private int age;

    private String name;

    private transient int code;

    private String email;

    public BaseBean() {
    }

    @Override
    public String toString() {
        return "BaseBean{" + "age=" + age + ", name='" + name + '\'' + ", code=" + code + ", email='" + email +
                '\'' + '}';
    }
}
@Test
public void testObjGson() {
    BaseBean bean = new BaseBean();
    bean.age = 10;
    bean.code = 20;
    bean.name = "一灰灰blog";

    Gson gson = new Gson();
    String str = gson.toJson(bean);
    System.out.println("json str: " + str);

    BaseBean out = gson.fromJson(str, BaseBean.class);
    System.out.println("after deserialization: " + out);
}

上面的使用姿势和前面并没有什么本质的区别,接下来看一下输出结果

json str: {"age":10,"name":"一灰灰blog"}
after deserialization: BaseBean{age=10, name='一灰灰blog', code=0, email='null'}

请注意:

  • 输出 json 串时,null 和transient修饰的成员不会输出到 json 串中
  • 对象没有 Get/Set 方法,也依然可以反序列化(对象也没有继承自 Serialize 接口,当然我个人的观点是请继承 Serialize 接口)

如果我希望将 null 的成员,也可以输出到 json 串,可以如下操作

// 并不直接创建Gson对象,改用GsonBuilder
Gson gsonWithNUll = new GsonBuilder().serializeNulls().create();
System.out.println("serialize with null: " + gsonWithNUll.toJson(bean));

输出结果如下

serialize with null: {"age":10,"name":"一灰灰blog","email":null}

说明:如果希望扩展序列化方式,通过 GsonBuilder 来构建 Gson 对象是一个不错的选择

4. JsonObject 与 JsonArray

某些场景下我们可能并没有定义反序列化的数据对象,比如 FastJson 中的直接反序列化为JSONObject/JSONArray,然后手动获取数据进行业务处理,这种场景下,gson 可以如何支持呢?

Map map = new HashMap();
map.put("a", "hello world");
map.put(12, true);
map.put("array", Arrays.asList("a", "c", "f", 12));
map.put("obj", Maps.newHashMap("k", "v"));

Gson gson = new Gson();
String str = gson.toJson(map);

// 直接借助 JsonParser#parseString 来实现反序列化
JsonObject obj = JsonParser.parseString(str).getAsJsonObject();
String a = obj.get("a").getAsString();
boolean b = obj.get("12").getAsBoolean();
JsonArray ary = obj.get("array").getAsJsonArray();
JsonObject o = obj.get("obj").getAsJsonObject();

System.out.println("a:" + a + " b:" + b + " ary:" + ary + " o:" + o);

请注意,我们这里主要借助的是JsonParser.parseString方法,输入参数可以是 String 也可以是流,返回的是JsonElement对象,这个对象比较有意思,提供了一些基础的类型输出方法如

  • getAsString: 返回 String
  • getAsInt: 返回 int
  • getAsJsonArray: 返回 JsonArray(json 数组)
  • getAsJsonObject: 返回 JsonObject (Json 对象)
  • ...
image

5. 泛型序列化

以上属于常规的基本使用姿势,实际的工作中,关于泛型的序列化和反序列化可以说非常常见了,那么应该如何处理呢

public static class ResWrapper<T> {
    private T data;
    private int code;
    private String msg;
}

public static class User {
    private int age;
    private String name;
}

@Test
public void testGenri() {
    ResWrapper<User> wrapper = new ResWrapper<>();
    wrapper.code = 0;
    wrapper.msg = "name";

    User user = new User();
    user.age = 18;
    user.name = "一灰灰";

    wrapper.data = user;

    Gson gson = new Gson();
    String str = gson.toJson(wrapper);

    Type type = new TypeToken<ResWrapper<User>>() {}.getType();
    ResWrapper<User> out = gson.fromJson(str, type);
    System.out.println(out);
}
image

上面的核心在于 Type 的生成: new TypeToken<ResWrapper<User>>() {}.getType();

6. 进阶

以上内容基本上可以覆盖日常业务开发中 90%的场景,当然 gson 也支持一些更高级的功能

如 filed name 映射

  • @SerializedName("custom_naming")
private class SomeObject {
  @SerializedName("custom_naming")
  private final String someField;
  private final String someOtherField;

  public SomeObject(String a, String b) {
    this.someField = a;
    this.someOtherField = b;
  }
}

如版本支持

  • @Since(1.1)
public class VersionedClass {
  @Since(1.1) private final String newerField;
  @Since(1.0) private final String newField;
  private final String field;

  public VersionedClass() {
    this.newerField = "newer";
    this.newField = "new";
    this.field = "old";
  }
}

VersionedClass versionedObject = new VersionedClass();
Gson gson = new GsonBuilder().setVersion(1.0).create();
String jsonOutput = gson.toJson(versionedObject);
System.out.println(jsonOutput);
System.out.println(); // 输出: {"newField":"new","field":"old"}

gson = new Gson();
jsonOutput = gson.toJson(versionedObject);
System.out.println(jsonOutput); // 输出: {"newerField":"newer","newField":"new","field":"old"}

自定义的类型转换

GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(MyType.class, new DateTimeSerializer());
gson.registerTypeAdapter(MyType.class, new DateTimeDeserializer());

private class DateTimeSerializer implements JsonSerializer<DateTime> {
  public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
    return new JsonPrimitive(src.toString());
  }
}

private class DateTimeDeserializer implements JsonDeserializer<DateTime> {
  public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
      throws JsonParseException {
    return new DateTime(json.getAsJsonPrimitive().getAsString());
  }
}

可视化的 json 输出

Gson gson = new GsonBuilder().setPrettyPrinting().create();
String jsonOutput = gson.toJson(someObject);

II. 其他

1. 一灰灰 Bloghttps://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

一灰灰 blog

QrCode
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352