Tip #44: Parsing Java Time Objects with Gson
If you need to convert JSON to Java Objects (or vice versa) and encounter issues dealing with Java Time Objects (LocalDateTime, Instant…
), this tip is for you—even if you don’t use Gson. (For more on Gson, check out the official documentation here.
This week, I ran into a problem with an API endpoint response. Have you seen this error before?
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private final long java.time.Instant.seconds accessible: module java.base does not "opens java.time" to unnamed module
This error is caused by Java’s strong module encapsulation, where certain fields in classes like java.time.Instant
are not directly accessible. To overcome this, we can use a custom adapter to control how Gson serializes and deserializes Instant
values, bypassing direct field access.
Solution: Custom Adapter for Instant
Using a custom adapter, we can convert Instant
into a format Gson can handle. Here’s an example:
public class InstantAdapter extends TypeAdapter<Instant> {
@Override
public void write(JsonWriter out, Instant value) throws IOException {
out.value(value.toString());
}
@Override
public Instant read(JsonReader in) throws IOException {
return Instant.parse(in.nextString());
}
}
//Here how to use
Gson gson = new GsonBuilder()
.registerTypeAdapter(Instant.class, new InstantAdapter())
.create();
Error is gone! Everything should work now right? But now, I have a new error:
java.time.format.DateTimeParseException: Text '2024-11-05T14:25:45.384146' could not be parsed at index 26
Understanding the Issue: Missing Z
for UTC Time
This error happens because the Instant
parser expects the ISO-8601 format to end with a Z
(for UTC time). If the Z
is missing, the parser throws an exception.
By adding a quick check to append the Z
if it’s missing in our adapter, we can handle this format inconsistency gracefully. The final adapter takes both of these issues into account, making Gson parse Instant
values more robustly.
@Override
public Instant read(JsonReader in) throws IOException {
String instantString = in.nextString();
// Automatically append 'Z' for UTC if it's missing
if (!instantString.endsWith("Z")) {
instantString += "Z";
}
return Instant.parse(instantString);
}
With this solution, you can reliably parse Instant
fields in your JSON, regardless of whether they include the Z
suffix. Try it out, and let me know if it helps!
Happy coding!