The error InaccessibleObjectException with the message “Failed making field ‘java.time.LocalDate#year’ accessible” occurs when Gson serializes or deserializes a class that has a field of a Java 8 date-time class such as LocalDate or LocalDateTime. A custom TypeAdapter for the date-time class fixes the error. We write one for LocalDate in this post and show the adapters for LocalDateTime and ZonedDateTime as well.
1. Problem
Gson reads and writes the fields of a class through reflection. When a field of a JDK class has private access, the JDK blocks that access and Gson throws a JsonIOException at runtime.
The Java 8 date-time classes are immutable and declare their fields with private access. When we serialize a class that has a field of one of these types, we get the error.
For example, the following User class has 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;
=
}
Serializing an instance of User with Gson.toJson() throws 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 stack trace shows the cause. The module java.base does not open the package java.time to the unnamed module, so Gson cannot make the private field year of LocalDate accessible.
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
The error message recommends a custom TypeAdapter for the declaring type. For LocalDate, we write the class LocalDateTypeAdapter and register it with GsonBuilder.registerTypeAdapter().
Gson gson = new GsonBuilder()
.registerTypeAdapter(LocalDate.class, new LocalDateTypeAdapter())
.create();
String jsonString = gson.toJson(user);
The class LocalDateTypeAdapter implements the interfaces JsonSerializer and JsonDeserializer and provides the custom logic to serialize and deserialize a LocalDate.
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);
}
}
With the adapter registered, gson.toJson(user) serializes the User instance without the error.
3. More Type Adapters
Each date-time class needs its own type adapter. The same approach works for LocalDateTime and ZonedDateTime.
3.1. LocalDateTime
The class LocalDateTimeTypeAdapter is the type adapter for java.time.LocalDateTime.
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 class ZonedDateTimeTypeAdapter is the type adapter for java.time.ZonedDateTime.
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 !!
Thanks, it’s very help me
Can you make on for Instant class too
You can refer from this Link.
Thank you so much..any thing with respect to Instant seconds. i was facing an issue error:
JSONIOException: Failed making field ‘java.time.Instant.seconds’ accessible : either change its visibility or write a custom Typeadaptor for its declaring type