改装+ Gson:动态计算字段或调用构造函数

yqkkidmi  于 2023-02-22  发布在  其他
关注(0)|答案(2)|浏览(206)

当一个响应来与改造Map到一个模型与Gson我喜欢计算一个字段,而不是如果设置接收值。
当accessToken的响应返回时,如下所示:

public class UserTokenResponse {
    @SerializedName("access_token")
    @Expose
    private String accessToken;          //v^1.1#i^1#r...
    @SerializedName("expires_in")
    @Expose
    private long accessTokenExpiresIn;   //7200
    //Constructor... setter ... getter
}

这里的问题是字段accessTokenExpiresIn 7200。我希望它按如下方式计算:

public UserTokenResponse (...) {        //Constructor
    this.accessTokenExpiresIn = System.currentTimeMillis() + 
    (accessTokenExpiresIn * 1000L);
}

这样它就变成了一个Unix时间戳,实际上是以毫秒为单位,但是由于构造函数没有被调用,所以这个字段将是7200。

xdyibdwo

xdyibdwo1#

您可以尝试使用一个定制的GSON反序列化器,如answer中所述。最后一个链接显示了如何使用定制的反序列化器创建一个定制的GSON工厂,以及如何将其附加到一个Retrofit示例。
基本上,您将在反序列化器方法中更改令牌响应的值。

axr492tv

axr492tv2#

对此最简单的解决方案可能是在字段上使用Gson的@JsonAdapter,并指定一个类型适配器来执行此计算。

class ExpirationAdapter extends TypeAdapter<Long> {
    // No-args constructor called by Gson
    public ExpirationAdapter() { }

    @Override
    public void write(JsonWriter out, Long value) throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public Long read(JsonReader in) throws IOException {
        long expiration = in.nextLong();
        return System.currentTimeMillis() + (expiration * 1000L);
    }
}

您的UserTokenResponse类将如下所示:

class UserTokenResponse {
    @SerializedName("expires_in")
    @Expose
    @JsonAdapter(ExpirationAdapter.class)
    private long accessTokenExpiresIn;

    // ...
}

您甚至可以将字段类型更改为java.time.Instant,这可能比使用long更容易,也更不容易出错,然后相应地调整适配器:

class ExpirationAdapter extends TypeAdapter<Instant> {
    // No-args constructor called by Gson
    public ExpirationAdapter() { }

    @Override
    public void write(JsonWriter out, Instant value) throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public Instant read(JsonReader in) throws IOException {
        long expiration = in.nextLong();
        return Instant.now().plusSeconds(expiration);
    }
}

一般来说,还有以下其他解决方案,但与使用@JsonAdapter相比,它们有一些很大的缺点:

  • 注册字段类型的适配器

实际上并不可行,因为字段的类型为long,然后您将覆盖Gson对long值的默认处理,即使对于完全不相关的字段,也不需要进行这种到期时间戳转换。

  • 为字段的声明类型注册适配器

UserTokenResponse注册一个类型是可能的,但是你必须手动执行类的反序列化,并且还必须手动处理诸如@SerializedName的注解,或者你必须编写一个TypeAdapterFactory来执行默认的反序列化,然后调整字段值。但是这将例如不允许将字段类型改变为java.time.Instant
还要注意Gson不调用setter方法或构造函数,它直接设置字段值。因此,如果可能,您应该始终提供一个无参数构造函数,否则Gson尝试创建一个示例而不调用构造函数,这可能会导致混淆错误,请参阅GsonBuilder.disableJdkUnsafe()了解更多信息。

相关问题