切換語言為:簡體

Jackson與GSON的深度對比

  • 爱糖宝
  • 2024-11-15
  • 2026
  • 0
  • 0

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(如JsonParserJsonGenerator)提供了細粒度的數據處理,適合在效能要求較高的場景使用。Gson則使用記憶體樹模型處理JSON,這在記憶體開銷和解析速度上較劣勢。

3. 原始碼實現分析

接下來,V 哥透過分析兩個元件的核心原始碼實現,來看一下兩者的不同之處。

Jackson 原始碼實現

Jackson實現基於三個核心模組:jackson-corejackson-databindjackson-annotations

  • 流式解析器:Jackson在jackson-core中實現了高效的流式解析器JsonParser和生成器JsonGenerator。這些解析器可以在讀取和寫入時逐行處理資料,避免不必要的物件建立,減少記憶體使用。

  • 資料繫結jackson-databind負責物件-JSON之間的轉換,透過反射和註解處理。

  • 擴充套件支援:透過模組化架構,Jackson可以擴充套件其他格式(如XML和YAML)。其實現透過Module介面定義和動態注入,實現了靈活的格式支援。

下面小V透過原始碼分析來具體介紹:

Jackson 的原始碼實現涉及多個模組(如 jackson-corejackson-databindjackson-annotations 等),我們可以從 Jackson 的資料解析與生成的基本流程、資料繫結模組的實現、註解處理的方式、流式 API 等方面逐步分析其原始碼。

1. 流式 API(Streaming API)分析

Jackson 使用了流式解析器 JsonParser 和生成器 JsonGenerator,這使得它在處理大資料量 JSON 時具有較好的效能。jackson-core模組提供了這兩個主要類。Jackson 的流式 API 是逐位元組處理 JSON 資料的,因此可以實現低記憶體消耗和高效的數據處理。

程式碼解析示例

jackson-coreJsonParser 類中,實現了對 JSON 資料的逐步解析。它採用“令牌(Token)”的形式進行解析,常見的 Token 包括 START_OBJECTFIELD_NAMEVALUE_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 轉換為物件。透過 DeserializationContextJsonDeserializer 的配合,它能夠解析複雜的 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 字串。它會先透過 SerializationContextJsonSerializer 獲取到需要的 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 等,用於精細控制序列化和反序列化的過程。註解的處理主要依賴 AnnotationIntrospectorBeanSerializerFactory 等類。

程式碼解析示例

在 Jackson 中,@JsonProperty 註解透過 AnnotationIntrospectorfindPropertyNameForParam() 方法來獲取指定的欄位名,並對 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 資料,例如 JsonNodeObjectNode 類,支援更靈活的資料訪問與修改。樹模型操作適合需要動態或未知 JSON 結構的場景。

  • JsonNode 是 Jackson 樹模型的核心介面,ObjectNodeArrayNode 是其實現類,用於儲存 JSON 物件和陣列。

ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonString);  // 讀取 JSON 字串
String name = rootNode.get("name").asText();       // 訪問欄位

JsonNode 的實現中,Jackson 提供了不同的子類來處理 JSON 資料節點,比如 TextNodeBooleanNodeArrayNode 等。這種設計使得 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 物件的序列化和反序列化。其實現涉及的核心類包括 GsonTypeAdapterJsonElementJsonParser 等。下面我們分步驟分析 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 節點,它是一個抽象類,具有 JsonObjectJsonArrayJsonPrimitiveJsonNull 等子類。

  • JsonParser 類用於解析 JSON 字串並返回一個 JsonElement,適用於不確定 JSON 結構的動態解析場景。

JsonElement jsonTree = JsonParser.parseString(jsonString);
if (jsonTree.isJsonObject()) {
    JsonObject jsonObject = jsonTree.getAsJsonObject();
    // 可以根據鍵值訪問
    String name = jsonObject.get("name").getAsString();
}

7. 自定義序列化與反序列化

Gson 允許使用者透過 TypeAdapterJsonSerializer/JsonDeserializer 來自定義序列化和反序列化過程,適用於處理特殊格式的資料。

  • JsonSerializerJsonDeserializer 是介面,用於自定義序列化和反序列化過程,使用者可以實現這兩個介面,並將其傳入 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 中新增自定義的 TypeAdapterJsonSerializerJsonDeserializer,並設定序列化/反序列化的策略。

GsonBuilder builder = new GsonBuilder();
builder.setPrettyPrinting(); // 格式化 JSON 輸出
builder.registerTypeAdapter(CustomClass.class, new CustomTypeAdapter());
Gson gson = builder.create(); // 建立 Gson 例項

9. 小結一下

透過以上的原始碼分析,咱們可以看到,Gson 的原始碼設計相對簡潔,適合處理簡單的 JSON 數據結構。它的核心設計思想圍繞 TypeAdapterTypeToken 和註解反射來實現靈活的序列化和反序列化。

4. 優缺點分析

Jackson和Gson各有優缺點,這也符合天下技術唯有最適合沒有最好的道理, V 哥建議,在選擇使用時,需要根據自己的專案情況來判斷,纔是明智的。

Jackson 優缺點

  • 優點

    • 效能出色,尤其是流式API適合大資料量解析。

    • 註解功能豐富,支援更細粒度的資料控制。

    • 支援多種資料格式,具有較強的擴充套件性。

    • 模組化架構,便於擴充套件和定製。

  • 缺點

    • API較複雜,學習成本較高。

    • 庫的大小比Gson略大。

Gson 優缺點

  • 優點

    • 輕量級、易用,便於快速整合。

    • 註解支援基礎操作,在簡單對映場景中靈活性較高。

    • 更易於除錯,且使用記憶體樹模型方便解析和修改JSON。

  • 缺點

    • 效能相對較差,不適合大資料量和高併發處理場景。

    • 對複雜場景的支援較弱,尤其是序列化和反序列化定製能力欠缺。

    • 不支援流式API,記憶體開銷較大。

5. 適用場景

從效能的角度來分析,咱們可以得出以下結論:

  • Jackson:適用於高併發、大資料量、高效能要求的場景,或需要複雜資料格式支援的應用。

  • Gson:適用於小規模的JSON處理、專案簡單數據傳輸、快速開發等輕量級場景。

Jackson和Gson各有所長,選擇時應根據具體需求權衡效能、靈活性和開發便捷性。

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.