Retrofit + Gson:如何动态计算字段或触发构造方法执行
解决Retrofit+Gson中动态计算实体字段值的问题
问题原因
Gson默认的反序列化逻辑不会调用你自定义的带参构造方法——它会优先使用无参构造创建实例(如果没有无参构造,会通过Unsafe机制直接实例化对象),再通过反射直接给字段赋值,所以你在构造方法里写的时间戳计算代码根本不会执行。
方案一:在Getter中动态计算(最简单)
不需要修改Gson配置,直接保留原始字段,在Getter方法里返回计算后的结果:
public class UserTokenResponse { @SerializedName("access_token") @Expose private String accessToken; @SerializedName("expires_in") @Expose private long rawExpiresIn; // 保留接口返回的原始秒数 // 必须保留无参构造,供Gson反序列化使用 public UserTokenResponse() {} // 对外提供的Getter返回计算后的时间戳 public long getAccessTokenExpiresIn() { return System.currentTimeMillis() + (rawExpiresIn * 1000L); } // 其他Getter/Setter public String getAccessToken() { return accessToken; } public void setAccessToken(String accessToken) { this.accessToken = accessToken; } // 内部用的原始字段Getter(可选) public long getRawExpiresIn() { return rawExpiresIn; } public void setRawExpiresIn(long rawExpiresIn) { this.rawExpiresIn = rawExpiresIn; } }
方案二:自定义Gson反序列化器(TypeAdapter)
如果希望字段本身存储的就是计算后的时间戳,可以自定义TypeAdapter,在反序列化阶段直接转换值:
1. 编写自定义TypeAdapter
public class UserTokenResponseAdapter extends TypeAdapter<UserTokenResponse> { @Override public void write(JsonWriter out, UserTokenResponse value) throws IOException { // 若需要序列化该实体,这里实现将时间戳转回秒数的逻辑 out.beginObject(); out.name("access_token").value(value.getAccessToken()); long expiresInSeconds = (value.getAccessTokenExpiresIn() - System.currentTimeMillis()) / 1000L; out.name("expires_in").value(expiresInSeconds); out.endObject(); } @Override public UserTokenResponse read(JsonReader in) throws IOException { UserTokenResponse response = new UserTokenResponse(); in.beginObject(); while (in.hasNext()) { String fieldName = in.nextName(); switch (fieldName) { case "access_token": response.setAccessToken(in.nextString()); break; case "expires_in": long expiresInSeconds = in.nextLong(); // 直接计算并设置最终的过期时间戳 response.setAccessTokenExpiresIn(System.currentTimeMillis() + expiresInSeconds * 1000L); break; default: in.skipValue(); // 跳过未知字段,避免解析错误 break; } } in.endObject(); return response; } }
2. 注册Adapter到Gson
两种注册方式选其一即可:
方式A:通过GsonBuilder全局注册
// 创建配置好的Gson实例 Gson customGson = new GsonBuilder() .registerTypeAdapter(UserTokenResponse.class, new UserTokenResponseAdapter()) .create(); // 将该Gson传入Retrofit的转换器 Retrofit retrofit = new Retrofit.Builder() .baseUrl("你的接口基础地址") .addConverterFactory(GsonConverterFactory.create(customGson)) .build();
方式B:在实体类上添加@JsonAdapter注解
@JsonAdapter(UserTokenResponseAdapter.class) public class UserTokenResponse { @SerializedName("access_token") @Expose private String accessToken; private long accessTokenExpiresIn; // 存储计算后的时间戳,无需SerializedName // Getter/Setter public String getAccessToken() { return accessToken; } public void setAccessToken(String accessToken) { this.accessToken = accessToken; } public long getAccessTokenExpiresIn() { return accessTokenExpiresIn; } public void setAccessTokenExpiresIn(long accessTokenExpiresIn) { this.accessTokenExpiresIn = accessTokenExpiresIn; } }
内容的提问来源于stack exchange,提问作者S. Gissel
相关产品推荐
相关产品推荐

