gson
# TypeAdapter
# Constructor
public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
final Type type = typeToken.getType();
final Class<? super T> rawType = typeToken.getRawType();
// first try an instance creator
@SuppressWarnings("unchecked") // types must agree
final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);
if (typeCreator != null) {
return new ObjectConstructor<T>() {
@Override public T construct() {
return typeCreator.createInstance(type);
}
};
}
// Next try raw type match for instance creators
@SuppressWarnings("unchecked") // types must agree
final InstanceCreator<T> rawTypeCreator =
(InstanceCreator<T>) instanceCreators.get(rawType);
if (rawTypeCreator != null) {
return new ObjectConstructor<T>() {
@Override public T construct() {
return rawTypeCreator.createInstance(type);
}
};
}
ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
if (defaultConstructor != null) {
return defaultConstructor;
}
ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
if (defaultImplementation != null) {
return defaultImplementation;
}
// finally try unsafe
return newUnsafeAllocator(type, rawType);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
- 使用我们自定义的 InstanceCreator, 可以在初始化时加入它;
- 使用默认构造器, 也就是无参构造函数;
- 如果是 Collection 或 Map, 则返回对应的对象;
- 使用 UnSafe
# UnSafe
// Do sneaky things to allocate objects without invoking their constructors.
public abstract class UnsafeAllocator {
...
public static UnsafeAllocator create() {
// try JVM
// public class Unsafe {
// public Object allocateInstance(Class<?> type);
// }
try {
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
Field f = unsafeClass.getDeclaredField("theUnsafe");
f.setAccessible(true);
final Object unsafe = f.get(null);
final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
return new UnsafeAllocator() {
@Override
@SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> c) throws Exception {
assertInstantiable(c);
return (T) allocateInstance.invoke(unsafe, c);
}
};
} catch (Exception ignored) {
}
// try dalvikvm, post-gingerbread
// public class ObjectStreamClass {
// private static native int getConstructorId(Class<?> c);
// private static native Object newInstance(Class<?> instantiationClass, int methodId);
// }
...
// try dalvikvm, pre-gingerbread
// public class ObjectInputStream {
// private static native Object newInstance(
// Class<?> instantiationClass, Class<?> constructorClass);
// }
...
// give up
...
}
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# ReflectiveTypeAdapterFactory
# ReflectiveTypeAdapterFactory 的工作原理
- 获取字段信息:
- 使用反射遍历 Java 类中的字段,包括私有字段
- 根据
@SerializedName注解决定字段在 JSON 中的名称 - 根据字段的类型和 Gson 的配置(如 exclusionStrategy)决定是否序列化或反序列化该字段
- 创建适配器:
- 调用
create()方法,根据字段信息生成一个 TypeAdapter,它负责字段的序列化和反序列化逻辑
- 序列化和反序列化:
- 在序列化时,ReflectiveTypeAdapterFactory 使用字段的值填充到 JSON
- 在反序列化时,它从 JSON 中提取字段值并赋值给 Java 对象
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type)
1
- 参数:
- gson:主 Gson 实例,用于递归处理复杂类型
- type:目标类型的 TypeToken,包含泛型信息
返回值:一个为指定类型生成的 TypeAdapter
逻辑:
- 检查类是否被排除(例如通过 @Expose 或 exclusionStrategy 设置)
- 遍历类的字段,为每个字段创建一个 BoundField(绑定字段),表示该字段的序列化/反序列化逻辑
- 返回一个新的 TypeAdapter 实例,该适配器基于字段的 BoundField 集合
BoundField
BoundField 是 ReflectiveTypeAdapterFactory 的内部类,用于绑定单个字段与 JSON 字段的映射关系
- 字段:
- name:字段对应的 JSON 名称
- typeAdapter:字段对应的 TypeAdapter
- serialized 和 deserialized:标识字段是否参与序列化/反序列化
- 方法:
write(JsonWriter out, Object value):负责将字段值写入 JSONread(JsonReader in):从 JSON 读取字段值并赋值给对象
- 自动化:
- 通过反射自动处理字段,无需手动编写适配器
- 注解支持:
- 支持注解如 @SerializedName 和 @Expose,便于自定义字段映射和排除策略
- 灵活性:
- 与 Gson 的其他工厂和自定义适配器结合使用,能覆盖各种复杂场景
- 性能优化:
- 尽管使用了反射,但在适配器创建阶段会缓存结果,避免重复开销
注意事项
- 性能问题:
- 由于
ReflectiveTypeAdapterFactory使用反射,性能比手写适配器稍逊 - 如果对性能有极高要求,可以考虑手动创建自定义 TypeAdapter
- 字段的可见性:
- 私有字段也可以被反射访问
- 如果字段未标注
@Expose并且启用了exclusionStrategy,它可能被排除
- 循环引用:
- 如果对象间存在循环引用,Gson 默认支持解决,但需要测试特殊情况下的行为
# other
# 找到 gson 解析失败的错误点
Expected BEGIN_OBJECT but was BEGIN_ARRAY
在使用 Gson 解析 JSON 数据时,Expected BEGIN_OBJECT but was BEGIN_ARRAY 表示 Gson 期望某个值是 JSON 对象({}),但实际遇到了 JSON 数组([])。可惜的是,Gson 并不会直接告诉你哪个键(key)导致了问题。不过,可以采取一些方法来定位问题
定位问题的常见方法:- 启用更详细的错误栈信息
确保异常中包含完整的 JSON 路径信息:
- 如果异常中没有包含路径信息,可能是因为你的 Gson 版本较低,建议升级到较新版本(如 2.8.6 或更新版本)
- 栈追踪中通常会标记触发异常的位置,可以根据栈信息找到与解析对象对应的代码位置
- 手动检查 JSON 和数据模型是否一致
- 根据 JSON 的结构逐一检查字段
- 确保数据类的字段与 JSON 的实际类型相符(例如对象 vs 数组)
- 重点检查字段类型定义为 Object 的地方,这类字段容易出错
- 使用自定义反序列化器
使用 Gson 的自定义 JsonDeserializer 可以帮助捕获错误的 JSON 数据并定位问题:
import com.google.gson.*;
import java.lang.reflect.Type;
public class DebugDeserializer<T> implements JsonDeserializer<T> {
private final Class<T> clazz;
public DebugDeserializer(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
return new Gson().fromJson(json, clazz);
} catch (JsonParseException e) {
System.err.println("Error while parsing JSON for type: " + clazz.getName());
System.err.println("Offending JSON: " + json);
throw e;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
用法:
Gson gson = new GsonBuilder()
.registerTypeAdapter(YourClass.class, new DebugDeserializer<>(YourClass.class))
.create();
YourClass obj = gson.fromJson(jsonString, YourClass.class);
1
2
3
4
5
2
3
4
5
输出错误信息会包含具体的 JSON 片段,从而帮助你找到问题字段
- 使用 JsonReader 检查具体问题
JsonReader 是 Gson 提供的低级工具,可以逐步解析 JSON 数据,捕获问题位置:
import com.google.gson.stream.JsonReader;
import java.io.StringReader;
public void debugJson(String json) {
JsonReader reader = new JsonReader(new StringReader(json));
try {
while (reader.hasNext()) {
System.out.println("Current JSON token: " + reader.peek());
reader.skipValue(); // 逐步读取数据
}
} catch (Exception e) {
e.printStackTrace();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
在解析到问题位置时,reader.peek() 会暴露当前解析的 JSON 类型,帮助判断是否类型不匹配
- 逐步排查字段
如果 JSON 比较大或复杂,可以按以下方式逐步排查:
- 先从最外层开始解析,确认外层结构是否正确
- 使用单独的解析逻辑逐一解析内层字段,捕获出错的部分
JsonObject jsonObject = new JsonParser().parse(jsonString).getAsJsonObject();
JsonArray array = jsonObject.getAsJsonArray("keyName"); // 测试是否为数组
1
2
2
- 检查 JSON 示例
JSON 示例:
{
"user": { "name": "John" },
"items": [{ "id": 1 }, { "id": 2 }]
}
1
2
3
4
2
3
4
Java 类(可能的问题):
class Data {
User user;
Item items; // 这里应为 List<Item>,但被误定义为单个对象
}
class User {
String name;
}
class Item {
int id;
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
修复方法: 将 Item items; 改为 List<Item> items;
- 常用 Gson 配置避免崩溃
如果无法确保 JSON 格式完全匹配,可以设置 Gson 的宽松模式以避免直接抛出异常:
Gson gson = new GsonBuilder()
.setLenient() // 宽松解析模式
.create();
1
2
3
2
3
Gson 本身不会直接告诉你哪个键导致了问题,但可以通过以下方法快速找到:
- 使用自定义反序列化器或低级工具(JsonReader)调试
- 检查字段定义和 JSON 的实际结构是否一致
- 升级 Gson 版本以确保更清晰的错误报告
# org.json.JSONArray 带来的解析问题
- 字段类型和 JSON 数据结构不匹配:
- Gson 期望一个 JSONArray 字段应以 JsonArray 类型处理,而非直接使用
org.json.JSONArray
- Gson 不直接支持
org.json.JSONArray:
- Gson 是专门处理其内部的 JSON 结构(如
com.google.gson.JsonObject和com.google.gson.JsonArray)的,org.json.JSONArray并非 Gson 的原生支持类型. 这回导致,在解析过程中将内容默认成 o bject, 从而导致不可预知的错误
在使用 gson 模块库的时候,尽量使用内部支持的类型
# link
上次更新: 2025/07/17, 17:31:43