The easiest solution to this is probably to use Gson's @JsonAdapter
on the field and to specify a type adapter which performs this calculation. The adapter could then for example look like this:
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);
}
}
And your UserTokenResponse
class would look like this:
class UserTokenResponse {
@SerializedName("expires_in")
@Expose
@JsonAdapter(ExpirationAdapter.class)
private long accessTokenExpiresIn;
// ...
}
You could even change the field type to java.time.Instant
, which might be easier and less error-prone to work with than just a long
, and adjust the adapter accordingly:
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);
}
}
In general there are also the following other solutions, but compared to using @JsonAdapter
they have some big drawbacks:
- Registering an adapter for the type of the field
Not really feasible because the field has type long
and you would then override Gson's default handling for long
values, even for completely unrelated fields where this expiration timestamp conversion is not desired.
- Registering an adapter for the declaring type of the field
Registering a type for UserTokenResponse
would be possible, but you would have to manually perform the deserialization of the class, and would have to handle annotations such as @SerializedName
manually as well. Or you would have to write a TypeAdapterFactory
which performs the default deserialization and then afterwards adjusts the field value, but that would for example not allow changing the field type to java.time.Instant
.
Also note that Gson does not call setter methods or constructors, it directly sets the field values. So if possible you should always provide a no-args constructor, otherwise Gson tries to create an instance without calling a constructor, which can lead to confusing errors, see GsonBuilder.disableJdkUnsafe()
for more information.