拆解Gson內核:當JSON遇上設計模式的浪漫邂逅
最近在一個項目中,我像拆快遞一樣把JSON解析模塊從原生換成了Gson。這個過程中順便看了下Gson源碼里隱藏的設計美學,就像發現快遞盒里居然裝著巧克力!今天咱們就打開這個甜蜜的包裝,看看Gson是怎么用設計模式玩轉JSON的。
Gson:數據世界的翻譯官
Gson就像個語言天才,能瞬間把JSON字符串變成對象(反序列化),也能把對象打包成JSON包裹(序列化)。舉個栗子,當服務器扔給你這樣一坨數據:
{
"name": "小明",
"age": 18,
"hobbies": ["coding", "gaming"]
}
Gson能瞬間把它變成:
Coder coder = new Gson().formJson(json, Code.class):
核心機密:TypeAdapter聯盟
適配器界的變形金剛
TypeAdapter就像是不同部門的專員,每個類型都有自己的專屬處理員。比如遇到Date類型,就派Date專員;遇到自定義的User類,就派反射專員。
// 以Date類型為例的簡化版適配器
public class DateTypeAdapter extends TypeAdapter<Date> {
private final DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
@Override
public void write(JsonWriter out, Date value) throws IOException {
out.value(format.format(value)); // 把Date變成字符串
}
@Override
public Date read(JsonReader in) throws IOException {
try {
return format.parse(in.nextString()); // 把字符串還原成Date
} catch (ParseException e) {
throw new JsonParseException(e);
}
}
}
反射處理的黑科技
當遇到自定義類時,反射適配器就開始騷操作了:
public class ReflectiveTypeAdapter<T> extends TypeAdapter<T> {
// 構造器工廠
private final ObjectConstructor<T> constructor;
// 字段綁定表(像快遞單號對照表)
private final Map<String, BoundField> boundFields;
@Override
public T read(JsonReader in) throws IOException {
T instance= constructor.construct(); // 像拆快遞一樣創建對象
in.beginObject();
while (in.hasNext()) {
String fieldName = in.nextName(); // 讀取字段名
BoundField field = boundFields.get(fieldName);
if (field != null) {
field.read(in, instance); // 按字段賦值
} else {
in.skipValue(); // 不認識的就扔掉
}
}
in.endObject();
return instance;
}
}
工廠流水線的魔法
Gson內部有個TypeAdapter生產流水線,用工廠模式高效制造適配器:
public class Gson {
// 適配器工廠列表(生產車間集合)
private final List<TypeAdapterFactory> factories;
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
// 走流水線逐個車間問能不能生產
for (TypeAdapterFactory factory : factories) {
TypeAdapter<T> candidate = factory.create(this, type);
if (candidate != null) {
return candidate; // 找到能生產的車間
}
}
throw new IllegalArgumentException("沒有適配器能處理: " + type);
}
}
自定義你的專屬翻譯
方案一:全能型選手(繼承TypeAdapter)
適合需要精確控制序列化和反序列化的場景:
public class UserAdapter extends TypeAdapter<User> {
@Override
public void write(JsonWriter out, User user) throws IOException {
out.beginObject();
out.name("fullName").value(user.getName()); // 字段重命名
out.name("age").value(user.getAge());
out.endObject();
}
@Override
public User read(JsonReader in) throws IOException {
User user = new User();
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case"fullName":
user.setName(in.nextString());
break;
case"age":
user.setAge(in.nextInt());
break;
}
}
in.endObject();
return user;
}
}
方案二:靈活派(實現接口)
適合只需要處理單方向的場景:
public class UserDeserializer implements JsonDeserializer<User> {
@Override
public User deserialize(JsonElement json, Type type,
JsonDeserializationContext context) {
JsonObject obj= json.getAsJsonObject();
User user=new User();
// 處理嵌套對象
user.setAddress(context.deserialize(obj.get("address"), Address.class));
// 轉換日期格式
user.setBirthday(new Date(obj.get("birthday").getAsLong()));
return user;
}
}
開發小貼士
日期處理的坑:建議統一注冊日期適配器
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create();
忽略未知字段:避免解析報錯
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
Gson像一支設計模式交響樂團:
? 適配器模式:解決接口兼容問題
? 工廠模式:靈活創建對象
? 反射機制:處理未知類型
? 建造者模式:優雅配置參數
調用gson.fromJson()時,不妨想象背后這支交響樂團正在為你演奏數據的魔法。好的庫設計就像樂譜,每個模式都在恰到好處的位置奏響和諧的旋律。