Jackson与GSON的深度对比
大家好,我是 V 哥。Jackson和Gson是Java中最常用的两个JSON解析库,它们在解析速度、灵活性、序列化/反序列化能力上各有特点。下面V 哥从功能特性、性能、源码实现等方面对比它们的优缺点。
V 哥推荐:2024 最适合入门的 JAVA 课程
1. 功能特性对比
-
Jackson:
- 提供全面的JSON处理支持,包括对象-JSON转换、树模型处理、流式处理等。
- 具有广泛的注解支持,方便进行序列化/反序列化控制(如
@JsonProperty
、@JsonIgnore
等)。 - 支持多种数据格式,包括XML、CSV、YAML等。
- 具备较强的扩展能力,通过模块化架构可以扩展额外功能。
-
Gson:
- Gson提供简单、易用的API和注解(如
@SerializedName
)。 - 在对象转换中可以自动处理空值,且支持Java泛型的序列化与反序列化。
- Gson原生支持Java对象的嵌套映射,且支持通过
TypeToken
来实现复杂类型的反序列化。 - 库文件轻量且易于集成。
- Gson提供简单、易用的API和注解(如
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各有所长,选择时应根据具体需求权衡性能、灵活性和开发便捷性。