You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 14:48:26