Jackson和Gson是Java中最常用的兩個JSON解析庫,它們在解析速度、靈活性、序列化/反序列化能力上各有特點。下面V 哥從功能特性、效能、原始碼實現等方面對比它們的優缺點。
1. 功能特性對比
Jackson:
提供全面的JSON處理支援,包括物件-JSON轉換、樹模型處理、流式處理等。
具有廣泛的註解支援,方便進行序列化/反序列化控制(如
@JsonProperty
、@JsonIgnore
等)。支援多種資料格式,包括XML、CSV、YAML等。
具備較強的擴充套件能力,透過模組化架構可以擴充套件額外功能。
Gson:
Gson提供簡單、易用的API和註解(如
@SerializedName
)。在物件轉換中可以自動處理空值,且支援Java泛型的序列化與反序列化。
Gson原生支援Java物件的巢狀對映,且支援透過
TypeToken
來實現複雜型別的反序列化。庫檔案輕量且易於整合。
2. 效能對比
一般來說,Jackson的效能優於Gson,尤其是在處理大資料量和複雜物件時,Jackson表現更佳。這主要歸因於Jackson內部的高效流式解析器和快取機制,它避免了記憶體中的大量臨時物件建立,提升了處理速度。
Jackson的流式API(如JsonParser
和JsonGenerator
)提供了細粒度的數據處理,適合在效能要求較高的場景使用。Gson則使用記憶體樹模型處理JSON,這在記憶體開銷和解析速度上較劣勢。
3. 原始碼實現分析
接下來,V 哥透過分析兩個元件的核心原始碼實現,來看一下兩者的不同之處。
Jackson 原始碼實現
Jackson實現基於三個核心模組:jackson-core
、jackson-databind
和jackson-annotations
。
流式解析器:Jackson在
jackson-core
中實現了高效的流式解析器JsonParser
和生成器JsonGenerator
。這些解析器可以在讀取和寫入時逐行處理資料,避免不必要的物件建立,減少記憶體使用。資料繫結:
jackson-databind
負責物件-JSON之間的轉換,透過反射和註解處理。擴充套件支援:透過模組化架構,Jackson可以擴充套件其他格式(如XML和YAML)。其實現透過
Module
介面定義和動態注入,實現了靈活的格式支援。
下面小V透過原始碼分析來具體介紹:
Jackson 的原始碼實現涉及多個模組(如 jackson-core
、jackson-databind
、jackson-annotations
等),我們可以從 Jackson 的資料解析與生成的基本流程、資料繫結模組的實現、註解處理的方式、流式 API 等方面逐步分析其原始碼。
1. 流式 API(Streaming API)分析
Jackson 使用了流式解析器 JsonParser
和生成器 JsonGenerator
,這使得它在處理大資料量 JSON 時具有較好的效能。jackson-core
模組提供了這兩個主要類。Jackson 的流式 API 是逐位元組處理 JSON 資料的,因此可以實現低記憶體消耗和高效的數據處理。
程式碼解析示例
在 jackson-core
的 JsonParser
類中,實現了對 JSON 資料的逐步解析。它採用“令牌(Token)”的形式進行解析,常見的 Token 包括 START_OBJECT
、FIELD_NAME
、VALUE_STRING
等。
JsonParser
類的核心方法nextToken()
:讀取下一個 JSON Token,並將其儲存在當前上下文中。
透過
switch-case
結構,處理 JSON 字串中的各類資料。例如,遇到
"{"
會生成START_OBJECT
,而"}"
會對應END_OBJECT
。
public JsonToken nextToken() throws IOException { // 實現不同字元的判斷邏輯,根據 JSON 資料逐步解析 int ch = nextByte(); switch (ch) { case '{': _currToken = JsonToken.START_OBJECT; return _currToken; case '}': _currToken = JsonToken.END_OBJECT; return _currToken; // 省略其他分支 } }
JsonGenerator
類則用於寫入 JSON,它將資料以 Token 的形式逐步生成,適用於輸出大檔案場景。
public void writeStartObject() throws IOException { _writeContext.writeStartObject(); _generator.writeStartObject(); }
2. 資料繫結(Data Binding)分析
Jackson 的 jackson-databind
模組負責物件和 JSON 之間的自動對映和繫結。核心實現類是 ObjectMapper
,它透過反射技術將 JSON 欄位自動對映到 Java 物件欄位。
程式碼解析示例
ObjectMapper
中主要透過 readValue()
和 writeValue()
方法來實現 JSON 與物件的互轉。
readValue()
方法用於將 JSON 轉換為物件。透過DeserializationContext
和JsonDeserializer
的配合,它能夠解析複雜的 JSON 資料到 Java 物件。
public <T> T readValue(JsonParser p, Class<T> valueType) throws IOException { JavaType javaType = _typeFactory.constructType(valueType); JsonDeserializer<Object> deser = _findRootDeserializer(_config, javaType); return (T) deser.deserialize(p, _deserializationContext); }
writeValue()
方法用於將物件轉換為 JSON 字串。它會先透過SerializationContext
和JsonSerializer
獲取到需要的 JSON 結構,然後透過JsonGenerator
寫入。
public void writeValue(JsonGenerator gen, Object value) throws IOException { JsonSerializer<Object> serializer = _serializerProvider().findTypedValueSerializer(value.getClass(), true, null); serializer.serialize(value, gen, _serializerProvider()); }
3. 註解處理
Jackson 支援豐富的註解,例如 @JsonProperty
、@JsonIgnore
等,用於精細控制序列化和反序列化的過程。註解的處理主要依賴 AnnotationIntrospector
和 BeanSerializerFactory
等類。
程式碼解析示例
在 Jackson 中,@JsonProperty
註解透過 AnnotationIntrospector
的 findPropertyNameForParam()
方法來獲取指定的欄位名,並對 Java 欄位進行對映。
AnnotationIntrospector
類中會檢查欄位上是否存在 Jackson 註解,如果存在則執行註解對應的序列化規則。
public String findPropertyNameForParam(AnnotatedParameter param) { JsonProperty ann = param.getAnnotation(JsonProperty.class); return (ann == null) ? null : ann.value(); }
BeanSerializerFactory
類會解析所有欄位和註解的關聯關係,並生成一個BeanSerializer
物件,用於在序列化時將註解資訊加入。
public JsonSerializer<Object> createBeanSerializer(SerializerProvider prov, JavaType type, BeanDescription beanDesc) { BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc); List<BeanPropertyWriter> props = findBeanProperties(prov, beanDesc, builder); builder.setProperties(props); return builder.build(); }
4. 樹模型(Tree Model)
Jackson 的樹模型允許使用者以樹形結構操作 JSON 資料,例如 JsonNode
和 ObjectNode
類,支援更靈活的資料訪問與修改。樹模型操作適合需要動態或未知 JSON 結構的場景。
JsonNode
是 Jackson 樹模型的核心介面,ObjectNode
和ArrayNode
是其實現類,用於儲存 JSON 物件和陣列。
ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode = mapper.readTree(jsonString); // 讀取 JSON 字串 String name = rootNode.get("name").asText(); // 訪問欄位
在 JsonNode
的實現中,Jackson 提供了不同的子類來處理 JSON 資料節點,比如 TextNode
、BooleanNode
、ArrayNode
等。這種設計使得 Jackson 能夠輕鬆地在樹節點中靈活讀取和寫入 JSON 資料。
5. 擴充套件與模組支援
Jackson 的模組化設計允許使用者載入第三方模組來擴充套件其功能。jackson-module
系列包含了對 JDK8 型別、Java時間類、Kotlin 等支援。這些模組透過 Module
類來註冊和管理,支援自定義序列化和反序列化器。
程式碼解析示例
透過
registerModule()
方法可以動態載入擴充套件模組。
ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); // 新增對 Java 時間類的支援
6. 小結一下
透過原始碼分析,V 哥發現,Jackson實在優秀,體現了優秀的設計架構,比如:流式解析提供了高效能支援,資料繫結實現了靈活的物件對映,註解處理讓開發者可以細粒度控制序列化過程,同時還支援擴充套件模組。這種設計使得 Jackson 適用於多種複雜的 JSON 處理場景。
Gson 原始碼實現
再來看 Gson,Gson 的原始碼實現相對簡潔,主要專注於 JSON 與 Java 物件的序列化和反序列化。其實現涉及的核心類包括 Gson
、TypeAdapter
、JsonElement
、JsonParser
等。下面我們分步驟分析 Gson 的主要原始碼實現過程。
1. Gson 的整體架構
Gson 的核心類是 Gson
,它負責 JSON 與 Java 物件之間的相互轉換。Gson
中的 toJson()
和 fromJson()
方法是核心介面,它們分別用於將 Java 物件轉換為 JSON 字串,以及將 JSON 字串解析為 Java 物件。Gson 的設計依賴於反射和註解,TypeAdapter
類用於自定義物件的序列化和反序列化。
2. JSON 轉 Java 物件(反序列化)
在 Gson
中,fromJson()
方法用於 JSON 到 Java 物件的轉換。其主要步驟包括解析 JSON、匹配型別、反射建立物件等。
程式碼解析示例
fromJson()
方法是 Gson 解析 JSON 字串的入口。在呼叫時會根據給定的型別,將 JSON 轉換為對應的 Java 物件。
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException { // 使用 JsonReader 逐字元讀取 JSON 資料 JsonReader jsonReader = new JsonReader(new StringReader(json)); return fromJson(jsonReader, classOfT); }
fromJson(JsonReader reader, Type typeOfT)
方法透過呼叫getAdapter(TypeToken.get(typeOfT))
來獲取TypeAdapter
,然後呼叫read()
方法讀取 JSON 資料並轉換為物件。
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException { TypeAdapter<T> adapter = getAdapter(TypeToken.get(typeOfT)); return adapter.read(reader); }
TypeAdapter
TypeAdapter
是 Gson 中的核心介面,用於控制物件序列化和反序列化的過程。Gson 提供了多種型別的 TypeAdapter
,並支援使用者自定義 TypeAdapter
。
read(JsonReader in)
方法會遍歷 JSON 資料並建立 Java 物件。例如,在ReflectiveTypeAdapterFactory.Adapter
中會透過反射讀取 JSON 資料並賦值給 Java 物件的欄位。
@Override public T read(JsonReader in) throws IOException { T instance = constructor.construct(); in.beginObject(); while (in.hasNext()) { String name = in.nextName(); // 找到欄位對應的 TypeAdapter,賦值給 Java 物件 Field field = fields.get(name); field.set(instance, fieldAdapter.read(in)); } in.endObject(); return instance; }
3. Java 物件轉 JSON(序列化)
Gson 的 toJson()
方法用於將 Java 物件序列化為 JSON 字串。實現思路類似,首先獲取物件的 TypeAdapter
,然後透過 write()
方法將資料寫入 JSON。
程式碼解析示例
toJson()
方法內部透過getAdapter()
獲取TypeAdapter
,然後呼叫write()
生成 JSON。
public void toJson(Object src, Appendable writer) throws JsonIOException { if (src != null) { toJson(src, src.getClass(), writer); } else { writer.append("null"); } }
write(JsonWriter out, T value)
方法用於將 Java 物件寫入 JSON。例如,在ReflectiveTypeAdapterFactory.Adapter
中,它會逐欄位將 Java 物件序列化為 JSON 字串。
@Override public void write(JsonWriter out, T value) throws IOException { out.beginObject(); for (BoundField boundField : boundFields.values()) { out.name(boundField.name); boundField.write(out, value); } out.endObject(); }
JsonWriter
JsonWriter
是 Gson 中流式輸出 JSON 的工具,配合 TypeAdapter
使用,它可以逐步生成 JSON,適合大物件和複雜結構。
4. TypeToken 的使用
Gson 使用 TypeToken
處理泛型,以解決 Java 的型別擦除問題。TypeToken
是 Gson 用於儲存泛型型別資訊的工具類。
例如
new TypeToken<List<String>>(){}.getType()
可以獲取List<String>
的Type
物件。
Type listType = new TypeToken<List<String>>(){}.getType(); List<String> list = gson.fromJson(json, listType);
TypeToken
的原始碼實現利用了匿名內部類來獲取泛型資訊,TypeToken
的構造方法會呼叫getSuperclassTypeParameter()
來捕獲泛型型別。
private static Type getSuperclassTypeParameter(Class<?> subclass) { Type superclass = subclass.getGenericSuperclass(); if (superclass instanceof ParameterizedType) { return ((ParameterizedType) superclass).getActualTypeArguments()[0]; } throw new RuntimeException("Missing type parameter."); }
5. 註解處理
Gson 支援註解 @SerializedName
來指定 JSON 欄位名,它透過反射獲取欄位上的註解並設定到 Field
物件中,以在序列化和反序列化時使用。
在
ReflectiveTypeAdapterFactory
中,透過反射獲取欄位的註解。如果欄位帶有@SerializedName
註解,則將註解指定的名稱作為 JSON 中的欄位名。
Field field = ...; SerializedName annotation = field.getAnnotation(SerializedName.class); String name = (annotation == null) ? field.getName() : annotation.value();
6. JsonElement 和 JsonParser
Gson 提供了 JsonElement
類來表示 JSON 節點,它是一個抽象類,具有 JsonObject
、JsonArray
、JsonPrimitive
和 JsonNull
等子類。
JsonParser
類用於解析 JSON 字串並返回一個JsonElement
,適用於不確定 JSON 結構的動態解析場景。
JsonElement jsonTree = JsonParser.parseString(jsonString); if (jsonTree.isJsonObject()) { JsonObject jsonObject = jsonTree.getAsJsonObject(); // 可以根據鍵值訪問 String name = jsonObject.get("name").getAsString(); }
7. 自定義序列化與反序列化
Gson 允許使用者透過 TypeAdapter
和 JsonSerializer
/JsonDeserializer
來自定義序列化和反序列化過程,適用於處理特殊格式的資料。
JsonSerializer
和JsonDeserializer
是介面,用於自定義序列化和反序列化過程,使用者可以實現這兩個介面,並將其傳入GsonBuilder
中進行註冊。
GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(Date.class, new JsonSerializer<Date>() { @Override public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { return new JsonPrimitive(src.getTime()); // 序列化為時間戳 } }); Gson gson = builder.create();
8. GsonBuilder
GsonBuilder
是 Gson 的構建類,用於配置 Gson 例項。可以在 GsonBuilder
中新增自定義的 TypeAdapter
、JsonSerializer
和 JsonDeserializer
,並設定序列化/反序列化的策略。
GsonBuilder builder = new GsonBuilder(); builder.setPrettyPrinting(); // 格式化 JSON 輸出 builder.registerTypeAdapter(CustomClass.class, new CustomTypeAdapter()); Gson gson = builder.create(); // 建立 Gson 例項
9. 小結一下
透過以上的原始碼分析,咱們可以看到,Gson 的原始碼設計相對簡潔,適合處理簡單的 JSON 數據結構。它的核心設計思想圍繞 TypeAdapter
、TypeToken
和註解反射來實現靈活的序列化和反序列化。
4. 優缺點分析
Jackson和Gson各有優缺點,這也符合天下技術唯有最適合沒有最好的道理, V 哥建議,在選擇使用時,需要根據自己的專案情況來判斷,纔是明智的。
Jackson 優缺點
優點:
效能出色,尤其是流式API適合大資料量解析。
註解功能豐富,支援更細粒度的資料控制。
支援多種資料格式,具有較強的擴充套件性。
模組化架構,便於擴充套件和定製。
缺點:
API較複雜,學習成本較高。
庫的大小比Gson略大。
Gson 優缺點
優點:
輕量級、易用,便於快速整合。
註解支援基礎操作,在簡單對映場景中靈活性較高。
更易於除錯,且使用記憶體樹模型方便解析和修改JSON。
缺點:
效能相對較差,不適合大資料量和高併發處理場景。
對複雜場景的支援較弱,尤其是序列化和反序列化定製能力欠缺。
不支援流式API,記憶體開銷較大。
5. 適用場景
從效能的角度來分析,咱們可以得出以下結論:
Jackson:適用於高併發、大資料量、高效能要求的場景,或需要複雜資料格式支援的應用。
Gson:適用於小規模的JSON處理、專案簡單數據傳輸、快速開發等輕量級場景。
Jackson和Gson各有所長,選擇時應根據具體需求權衡效能、靈活性和開發便捷性。