Jacky's blog
首页
  • 学习笔记

    • web
    • android
    • iOS
    • vue
  • 分类
  • 标签
  • 归档
收藏
  • tool
  • algo
  • python
  • java
  • server
  • growth
  • frida
  • blog
  • SP
  • more
GitHub (opens new window)

Jack Yang

编程; 随笔
首页
  • 学习笔记

    • web
    • android
    • iOS
    • vue
  • 分类
  • 标签
  • 归档
收藏
  • tool
  • algo
  • python
  • java
  • server
  • growth
  • frida
  • blog
  • SP
  • more
GitHub (opens new window)
  • tutorial
  • jetpack

  • components

  • androidx

  • 动态化
  • apm

  • module

    • android module
    • matrix-tencent
    • leakcanary
    • okhttp
    • j2oc
    • gson
      • TypeAdapter
      • Constructor
        • UnSafe
      • ReflectiveTypeAdapterFactory
        • ReflectiveTypeAdapterFactory 的工作原理
      • other
        • 找到 gson 解析失败的错误点
        • org.json.JSONArray 带来的解析问题
      • link
  • harmony

  • tool

  • other

  • kotlin

  • 《android》
  • module
Jacky
2024-10-29
目录

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
实例化对象
  1. 使用我们自定义的 InstanceCreator, 可以在初始化时加入它;
  2. 使用默认构造器, 也就是无参构造函数;
  3. 如果是 Collection 或 Map, 则返回对应的对象;
  4. 使用 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

# ReflectiveTypeAdapterFactory

# ReflectiveTypeAdapterFactory 的工作原理

  1. 获取字段信息:
  • 使用反射遍历 Java 类中的字段,包括私有字段
  • 根据 @SerializedName 注解决定字段在 JSON 中的名称
  • 根据字段的类型和 Gson 的配置(如 exclusionStrategy)决定是否序列化或反序列化该字段
  1. 创建适配器:
  • 调用 create() 方法,根据字段信息生成一个 TypeAdapter,它负责字段的序列化和反序列化逻辑
  1. 序列化和反序列化:
  • 在序列化时,ReflectiveTypeAdapterFactory 使用字段的值填充到 JSON
  • 在反序列化时,它从 JSON 中提取字段值并赋值给 Java 对象
核心方法
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type)
1
  1. 参数:
  • gson:主 Gson 实例,用于递归处理复杂类型
  • type:目标类型的 TypeToken,包含泛型信息
  1. 返回值:一个为指定类型生成的 TypeAdapter

  2. 逻辑:

  • 检查类是否被排除(例如通过 @Expose 或 exclusionStrategy 设置)
  • 遍历类的字段,为每个字段创建一个 BoundField(绑定字段),表示该字段的序列化/反序列化逻辑
  • 返回一个新的 TypeAdapter 实例,该适配器基于字段的 BoundField 集合

BoundField

BoundField 是 ReflectiveTypeAdapterFactory 的内部类,用于绑定单个字段与 JSON 字段的映射关系

  1. 字段:
  • name:字段对应的 JSON 名称
  • typeAdapter:字段对应的 TypeAdapter
  • serialized 和 deserialized:标识字段是否参与序列化/反序列化
  1. 方法:
  • write(JsonWriter out, Object value):负责将字段值写入 JSON
  • read(JsonReader in):从 JSON 读取字段值并赋值给对象
特性和优点
  1. 自动化:
  • 通过反射自动处理字段,无需手动编写适配器
  1. 注解支持:
  • 支持注解如 @SerializedName 和 @Expose,便于自定义字段映射和排除策略
  1. 灵活性:
  • 与 Gson 的其他工厂和自定义适配器结合使用,能覆盖各种复杂场景
  1. 性能优化:
  • 尽管使用了反射,但在适配器创建阶段会缓存结果,避免重复开销

注意事项
  1. 性能问题:
  • 由于 ReflectiveTypeAdapterFactory 使用反射,性能比手写适配器稍逊
  • 如果对性能有极高要求,可以考虑手动创建自定义 TypeAdapter
  1. 字段的可见性:
  • 私有字段也可以被反射访问
  • 如果字段未标注 @Expose 并且启用了 exclusionStrategy,它可能被排除
  1. 循环引用:
  • 如果对象间存在循环引用,Gson 默认支持解决,但需要测试特殊情况下的行为

# other

# 找到 gson 解析失败的错误点

Expected BEGIN_OBJECT but was BEGIN_ARRAY

在使用 Gson 解析 JSON 数据时,Expected BEGIN_OBJECT but was BEGIN_ARRAY 表示 Gson 期望某个值是 JSON 对象({}),但实际遇到了 JSON 数组([])。可惜的是,Gson 并不会直接告诉你哪个键(key)导致了问题。不过,可以采取一些方法来定位问题

定位问题的常见方法:
  1. 启用更详细的错误栈信息

确保异常中包含完整的 JSON 路径信息:

  • 如果异常中没有包含路径信息,可能是因为你的 Gson 版本较低,建议升级到较新版本(如 2.8.6 或更新版本)
  • 栈追踪中通常会标记触发异常的位置,可以根据栈信息找到与解析对象对应的代码位置
  1. 手动检查 JSON 和数据模型是否一致
  • 根据 JSON 的结构逐一检查字段
  • 确保数据类的字段与 JSON 的实际类型相符(例如对象 vs 数组)
  • 重点检查字段类型定义为 Object 的地方,这类字段容易出错
  1. 使用自定义反序列化器

使用 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

用法:

Gson gson = new GsonBuilder()
    .registerTypeAdapter(YourClass.class, new DebugDeserializer<>(YourClass.class))
    .create();

YourClass obj = gson.fromJson(jsonString, YourClass.class);
1
2
3
4
5

输出错误信息会包含具体的 JSON 片段,从而帮助你找到问题字段

  1. 使用 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

在解析到问题位置时,reader.peek() 会暴露当前解析的 JSON 类型,帮助判断是否类型不匹配

  1. 逐步排查字段

如果 JSON 比较大或复杂,可以按以下方式逐步排查:

  • 先从最外层开始解析,确认外层结构是否正确
  • 使用单独的解析逻辑逐一解析内层字段,捕获出错的部分
JsonObject jsonObject = new JsonParser().parse(jsonString).getAsJsonObject();
JsonArray array = jsonObject.getAsJsonArray("keyName"); // 测试是否为数组
1
2
  1. 检查 JSON 示例

JSON 示例:

{
    "user": { "name": "John" },
    "items": [{ "id": 1 }, { "id": 2 }]
}
1
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

修复方法: 将 Item items; 改为 List<Item> items;

  1. 常用 Gson 配置避免崩溃

如果无法确保 JSON 格式完全匹配,可以设置 Gson 的宽松模式以避免直接抛出异常:

Gson gson = new GsonBuilder()
    .setLenient() // 宽松解析模式
    .create();
1
2
3
总结

Gson 本身不会直接告诉你哪个键导致了问题,但可以通过以下方法快速找到:

  • 使用自定义反序列化器或低级工具(JsonReader)调试
  • 检查字段定义和 JSON 的实际结构是否一致
  • 升级 Gson 版本以确保更清晰的错误报告

# org.json.JSONArray 带来的解析问题

  1. 字段类型和 JSON 数据结构不匹配:
  • Gson 期望一个 JSONArray 字段应以 JsonArray 类型处理,而非直接使用 org.json.JSONArray
  1. Gson 不直接支持 org.json.JSONArray:
  • Gson 是专门处理其内部的 JSON 结构(如 com.google.gson.JsonObject 和 com.google.gson.JsonArray)的,org.json.JSONArray 并非 Gson 的原生支持类型. 这回导致,在解析过程中将内容默认成 o bject, 从而导致不可预知的错误
总结

在使用 gson 模块库的时候,尽量使用内部支持的类型

# link

  • other
    • 使用 Gson 解析 data class 引发的一点思考 (opens new window)
上次更新: 2025/07/17, 17:31:43
j2oc
HarmonyOS 鸿蒙开发完整指南

← j2oc HarmonyOS 鸿蒙开发完整指南→

最近更新
01
npx 使用指南
10-12
02
cursor
09-28
03
inspect
07-20
更多文章>
Theme by Vdoing | Copyright © 2019-2025 Jacky | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式