首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Gson 進階完全指南:Java 對象與 JSON 深度互轉、泛型處理、自定義適配器、Null 安全與最佳實踐

     編輯:Java 相關知識

前言

Gson 作為 Java 領域最流行的 JSON 庫之一,其功能遠不止基礎的序列化/反序列化。本文系統梳理 Gson 的進階特性:泛型 TypeToken、集合限制、巢狀類處理、自定義 TypeAdapter/InstanceCreator、Null 安全、版本控制、字段過濾策略、命名策略、線程安全的狀態共享,並給出生產級 NullStringToEmptyAdapterFactory 解決方案。

核心類與線程安全

  • Gson:無狀態,線程安全,可全局單例復用。
  • GsonBuilder:構建定制實例,建議在啟動階段構建好單例。

基礎類型與數組

Gson gson = new Gson();
gson.toJson(1);           // "1"
gson.toJson("abcd");      // "\"abcd\""
gson.toJson(new long[]{1,2,3}); // "[1,2,3]"
int[] arr = gson.fromJson("[1,2,3]", int[].class);

對象與泛型 — TypeToken 關鍵機制

痛點: Java 泛型擦除導致運行時丟失類型參數,foo.getClass() 僅返回 Foo.class,無法獲知 Foo<Bar>。

class Foo<T> { T value; }
Foo<Bar> foo = new Foo<Bar>();
gson.toJson(foo); // 可能無法正確序列化 value
gson.fromJson(json, foo.getClass()); // 無法將 value 反序列化為 Bar

// 解決:TypeToken 捕獲完整泛型類型
Type fooType = new TypeToken<Foo<Bar>>(){}.getType();
gson.toJson(foo, fooType);
gson.fromJson(json, fooType);
  • TypeToken 利用匿名內部類保留泛型簽名,getType() 返回 ParameterizedType。
  • 適用於任何泛型類型:List<T>、Map<K,V>、自定義泛型類。

集合處理與限制

序列化:任意類型集合直接支持

Collection<Integer> ints = Arrays.asList(1,2,3,4,5);
String json = gson.toJson(ints); // "[1,2,3,4,5]"

反序列化:必須指定泛型類型

Type type = new TypeToken<Collection<Integer>>(){}.getType();
Collection<Integer> ints2 = gson.fromJson(json, type);

混合類型集合的三種方案

JSON: ['hello', 5, {'name':'GREETINGS','source':'guest'}]

  1. 推薦:流式解析器 + 分項反序列化(JsonParser / JsonReader)逐元素處理,類型安全、性能最優。
  2. 註冊集合 TypeAdapter:將 Collection.class 映射到特定元素類型,副作用是影響其他集合反序列化。
  3. 統一元素類型:若業務允許,將集合元素類型統一為公共父類/接口,反序列化為 Collection<CommonType>。

巢狀類處理

  • 靜態內部類: 直接支持序列化/反序列化。
  • 非靜態內部類: 序列化 OK;反序列化失敗(需外部類實例)。解決:改為 static、或提供 InstanceCreator。
// InstanceCreator 示例
class InstanceCreatorForB implements InstanceCreator<A.B> {
    private final A a;
    public InstanceCreatorForB(A a) { this.a = a; }
    public A.B createInstance(Type type) { return a.new B(); }
}
Gson gson = new GsonBuilder().registerTypeAdapter(A.B.class, new InstanceCreatorForB(new A())).create();

自定義序列化/反序列化 — TypeAdapter

當默認行為不符合預期(如 Joda-Time DateTime、自定義 ID 類),編寫 TypeAdapter。

序列化器

class DateTimeSerializer implements JsonSerializer<DateTime> {
    public JsonElement serialize(DateTime src, Type type, JsonSerializationContext ctx) {
        return new JsonPrimitive(src.toString()); // 或自定義格式
    }
}

反序列化器

class DateTimeDeserializer implements JsonDeserializer<DateTime> {
    public DateTime deserialize(JsonElement json, Type type, JsonDeserializationContext ctx) {
        return new DateTime(json.getAsJsonPrimitive().getAsString());
    }
}

註冊

Gson gson = new GsonBuilder()
    .registerTypeAdapter(DateTime.class, new DateTimeSerializer())
    .registerTypeAdapter(DateTime.class, new DateTimeDeserializer())
    .create();
  • 同一類可註冊多個適配器,Gson 會檢查接口類型自動分發。
  • 泛型類適配器可通過 Type 參數獲取實際類型參數(如 Id<T> 的 T)。

InstanceCreator — 無無參構造函數類的實例化

class MoneyInstanceCreator implements InstanceCreator<Money> {
    public Money createInstance(Type type) { return new Money("1000000", CurrencyCode.USD); }
}
Gson gson = new GsonBuilder().registerTypeAdapter(Money.class, new MoneyInstanceCreator()).create();

Null 安全完整解決方案

問題

  • 默認:序列化忽略 null 字段,反序列化 null 設置為 null。
  • 痛點:前端/下游要求字段必須存在且為空字符串 "",而非 null 或缺失。

方案一:序列化輸出 null

Gson gson = new GsonBuilder().serializeNulls().create();
// {"name":null,"age":20}

方案二:反序列化 null → 空字符串(生產級推薦)

// 1. 工廠類
class NullStringToEmptyAdapterFactory implements TypeAdapterFactory {
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        if (type.getRawType() != String.class) return null;
        return (TypeAdapter<T>) new StringNullAdapter();
    }
}

// 2. 適配器
class StringNullAdapter extends TypeAdapter<String> {
    public String read(JsonReader reader) throws IOException {
        if (reader.peek() == JsonToken.NULL) { reader.nextNull(); return ""; }
        return reader.nextString();
    }
    public void write(JsonWriter writer, String value) throws IOException {
        if (value == null) { writer.nullValue(); return; }
        writer.value(value);
    }
}

// 3. 使用
Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(new NullStringToEmptyAdapterFactory())
    .create();
// 反序列化時 JSON null → Java ""
// 序列化時 Java null → JSON null (可再配合 serializeNulls())

版本控制 — @Since

class VersionedClass {
    @Since(1.1) String newerField;
    @Since(1.0) String newField;
    String field;
}
Gson gson = new GsonBuilder().setVersion(1.0).create();
// 僅序列化版本 ≤ 1.0 的字段

字段過濾三板斧

  1. 修飾符過濾: excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT)。
  2. @Expose 白名單: excludeFieldsWithoutExposeAnnotation() + 字段標註 @Expose。
  3. 自定義 ExclusionStrategy: 實現 shouldSkipClass/Field,支持任意邏輯(如按註解、類型、名稱模式過濾)。

輸出格式:緊湊 vs 美化

Gson gson = new GsonBuilder().setPrettyPrinting().create();
// 每行 80 字符,縮進 2 空格,左邊距 4 空格

線程安全的狀態共享

序列化器/反序列化器需共享狀態時:

  1. 靜態字段(非線程安全)
  2. 外部類字段(非線程安全)
  3. ThreadLocal(推薦,線程安全)

總結

Gson 以「核心簡潔、擴展強大」著稱。掌握 TypeToken 泛型捕獲、TypeAdapter 自定義轉換、NullStringToEmptyAdapterFactory Null 安全、ExclusionStrategy 靈活過濾 四大進階技能,即可應對極其複雜的 JSON 交互場景。建議專案建立 GsonConfig 集中管理實例,並編寫單元測試覆蓋關鍵轉換邏輯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved