Learn to fix the InaccessibleObjectException or “Failed making field ‘java.time.LocalDate#year’ accessible” type errors when serializing or deserializing the Java classes that are of new Java 8 Date time classes such as LocalDate or LocalDateTime.
1. Problem
By default, Gson wants the fields. that we are serializing/deserializing of public access. If the fields are of protected/private access, then it throws JsonIOException in runtime.
The new Java 8 date time classes, are immutable and define the fields with private access. When we serialize a class that has a field of these types, we get the error.
For example, in the following User class the field dateOfBirth of type LocalDate.
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
private Long id;
private String firstName;
private String lastName;
private LocalDate dateOfbirth;
=
}
When we serialize an instance of User, we get the error:
User user = new User(1L, "lokesh", "gupta", LocalDate.of(1999, Month.JANUARY, 1));
Gson gson = new Gson();
String jsonString = gson.toJson(user);
The runtime error is:
Exception in thread "main" com.google.gson.JsonIOException:
Failed making field 'java.time.LocalDate#year' accessible; either increase its visibility or write a custom TypeAdapter for its declaring type.
...
...
Caused by: java.lang.reflect.InaccessibleObjectException:
Unable to make field private final int java.time.LocalDate.year accessible: module java.base does not "opens java.time" to unnamed module @1e67b872
2. Solution
In such cases, Gson recommends defining TypeAdapter for the specific type that is causing the problem. In our case, we need to add the LocalDateAdapter.
Gson gson = new GsonBuilder()
.registerTypeAdapter(LocalDate.class, new LocalDateTypeAdapter())
.create();
String jsonString = gson.toJson(user);
The LocalDateTypeAdapter class implements JsonSerializer and JsonDeserializer interfaces and provides the custom logic for serializing and deserializing the instance.
public class LocalDateTypeAdapter implements JsonSerializer<LocalDate>, JsonDeserializer<LocalDate> {
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Override
public JsonElement serialize(final LocalDate date, final Type typeOfSrc,
final JsonSerializationContext context) {
return new JsonPrimitive(date.format(formatter));
}
@Override
public LocalDate deserialize(final JsonElement json, final Type typeOfT,
final JsonDeserializationContext context) throws JsonParseException {
return LocalDate.parse(json.getAsString(), formatter);
}
}
Now the application will run fine.
3. More Type Adapters
The following are the most common type adapters for other date-time classes.
3.1. LocalDateTime
The TypeAdapter for java.time.LocalDateTime class.
public class LocalDateTimeTypeAdapter implements JsonSerializer<LocalDateTime>, JsonDeserializer<LocalDateTime> {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss");
@Override
public JsonElement serialize(LocalDateTime localDateTime, Type srcType,
JsonSerializationContext context) {
return new JsonPrimitive(formatter.format(localDateTime));
}
@Override
public LocalDateTime deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
return LocalDateTime.parse(json.getAsString(), formatter);
}
}
3.2. ZonedDateTime
The TypeAdapter for java.time.ZonedDateTime class.
public class ZonedDateTimeTypeAdapter implements JsonSerializer<ZonedDateTime>, JsonDeserializer<ZonedDateTime> {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss z");
@Override
public JsonElement serialize(ZonedDateTime zonedDateTime, Type srcType,
JsonSerializationContext context) {
return new JsonPrimitive(formatter.format(zonedDateTime));
}
@Override
public ZonedDateTime deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
return ZonedDateTime.parse(json.getAsString(), formatter);
}
}
Happy Learning !!