eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – All Access – NPI EA (cat= Spring)
announcement - icon

All Access is finally out, with all of my Spring courses. Learn JUnit is out as well, and Learn Maven is coming fast. And, of course, quite a bit more affordable. Finally.

>> GET THE COURSE
Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Overview

Starting with version 9, Java places a strong emphasis on encapsulation. The major feature behind this is the Java Platform Module System (JPMS). It controls how our own packages are exposed and, at the same time, restricts access to Java’s internal classes. However, many libraries rely heavily on reflection to access private members. So we need ways to relax these restrictions in a controlled manner.

In this tutorial, we’ll learn how to avoid the InaccessibleObjectException when deserializing JSON with Gson. We’ll focus on modular projects and date handling.

2. Gson Status

Gson offers convenient tools for serializing objects to JSON and deserializing them back, and it relies on accessing private fields via reflection. With strong encapsulation in place, this no longer works seamlessly, either for our own objects or for Java’s internal classes. The latest Gson versions have made a small move towards public APIs, specifically for records and dates.

Gson is currently in maintenance mode. Therefore, we shouldn’t expect any new features, only bug fixes and security fixes.

3. Project Setup

Let’s examine the pom.xml we’ll use. We need Java 17 to demonstrate strong encapsulation and for its full support of records. Therefore, we set it in the properties section of pom.xml:

<maven.compiler.release>17</maven.compiler.release>

Next, let’s check the Gson version. We should use the latest one:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.14.0</version>
</dependency>

4. Gson and Java Modules

Let’s create a simple Java project named gson-module. We declare the gson.exception module in the module-info.java file:

module gson.exception {

    requires com.google.gson;
    requires org.slf4j;

    exports gson.exception;
}

With the requires statement, we state that our module needs Gson (and SLF4J for logging). Then, we make our own package gson.exception available to other modules with the exports statement.

Now let’s add some classes that describe a conference. The first is a good old POJO:

public class ConferencePojo {

    private String name;
    private int numberOfParticipants;

    // standard setters and getters
}

This object stores the name and number of participants of a conference. It has private fields and public getters and setters.

Next, we prepare an equivalent Java record:

public record ConferenceRecord(String name, int numberOfParticipants) {
}

With a record, we don’t need to declare the fields or write getters and setters.

4.1. Gson Failure With POJO

Gson easily deserializes a POJO such as ConferencePojo in a classpath project, but not in a module path one. To prove that, we’ll run the GsonModuleMain application inside our modular project. Let’s look at its main method:

public static void main(String[] args) {
    String moduleName = GsonModuleMain.class.getModule()
      .getName();

    if (moduleName == null) {
        log.info("Mode: [ Class Path ] (Class in the Unnamed Module)");
    } else {
        log.info("Mode: [ Module Path ] - Module name: " + moduleName);
    }

    Gson gson = new Gson();
    String json = "{\"name\":\"Java Conference\"}";

    try {
        ConferencePojo pojo = gson.fromJson(json, ConferencePojo.class);
        log.info("Deserialization successful! Object " + pojo);
    } catch (Exception e) {
        log.error("Expected exception caught!", e);
    }
}

At the beginning, we log whether the JVM actually loaded our class from the module path. Then we try to deserialize the POJO. Let’s examine the result:

10:40:06.766 [main] INFO gson.exception.GsonModuleMain -- Mode: [ Module Path ] - Module name: gson.exception
10:40:06.868 [main] ERROR gson.exception.GsonModuleMain -- Expected exception caught!
com.google.gson.JsonIOException: Failed making field 'gson.exception.ConferencePojo#name' accessible; either increase its visibility or write a custom TypeAdapter for its declaring type.
See https://github.com/google/gson/blob/main/Troubleshooting.md#reflection-inaccessible-to-module-gson
...
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private java.lang.String gson.exception.ConferencePojo.name accessible: module gson.exception does not "opens gson.exception" to module com.google.gson
...

Gson throws a JsonIOException caused by an InaccessibleObjectException. From the two messages, we learn what happened and get hints on how to solve the problem. In short, within a modular project, Gson can’t access the private fields of our class unless we let it.

4.2. Success With Records

With records, we’ll get a completely different result. Let’s use a JUnit 5 test to demonstrate it:

@Test
void givenModularAndExportedPackage_whenDeserializingRecord_thenSuccess() {
    String json = """
        {
            "name": "Java Conference",
            "numberOfParticipants": 150
        }
        """;

    Gson gson = new Gson();

    ConferenceRecord result = assertDoesNotThrow(() -> {
        return gson.fromJson(json, ConferenceRecord.class);
    });

    assertNotNull(result);
    assertEquals("Java Conference", result.name());
}

This time, Gson successfully deserializes the JSON and creates the record. Since version 2.10, Gson uses a record’s canonical constructor when instantiating the object, unlike classes, where Gson needs access to private fields.

5. How to Deserialize a POJO

If we still want to use a POJO, let’s follow the hints from the exception messages. We have four ways to fix this.

5.1. Make Fields Public

Let’s create a copy of ConferencePojo with public fields:

public class ConferencePojoPublic {

    public String name;
    public int numberOfParticipants;
}

Now Java lets Gson access the fields with reflection, and deserialization succeeds. However, we lose encapsulation this way.

5.2. opens to Expose Private Fields

To let Gson work with our private fields, we can open our package to it. Let’s create a new project, gson-module-opens, and look at its module-info.java file:

module gson.exception {

    requires com.google.gson;
    requires org.slf4j;

    opens gson.exception to com.google.gson;

    exports gson.exception;
}

With the statement opens gson.exception to com.google.gson, we allow Gson to access the private fields in the gson.exception package. Let’s run our GsonModuleMain application again:

19:08:55.369 [main] INFO gson.exception.GsonModuleMain -- Mode: [ Module Path ] - Module name: gson.exception
19:08:55.410 [main] INFO gson.exception.GsonModuleMain -- Deserialization successful! Object gson.exception.ConferencePojo@675d3402

This time, the program doesn’t throw an exception and deserializes the JSON successfully, even in the modular project.

5.3. Using the TypeAdapter Abstract Class

If we can’t use opens, we can extend Gson’s TypeAdapter abstract class for our POJO. All we need to do is implement its write and read methods. As we focus on deserialization, let’s look only at the read method:

@Override
public ConferencePojo read(JsonReader in) throws IOException {
    String name = null;
    int numberOfParticipants = 0;

    in.beginObject();
    while (in.hasNext()) {
        String key = in.nextName();
        if ("name".equals(key)) {
            name = in.nextString();
        } else if ("numberOfParticipants".equals(key)) {
            numberOfParticipants = in.nextInt();
        } else {
            in.skipValue();
        }
    }
    in.endObject();

    ConferencePojo result = new ConferencePojo();
    result.setName(name);
    result.setNumberOfParticipants(numberOfParticipants);

    return result;
}

The read method returns an instance of ConferencePojo. It loops over all the entries of the JsonReader and picks out the fields by their JSON names.

Note that we create the ConferencePojo object with its default constructor and fill it in with setters. This is the crucial point: to use a TypeAdapter, we need a public API for creating the object. In our case, that’s the setters, but a constructor, public fields, a factory, or a builder would work as well.

Now let’s test the adapter:

@Test
void whenAdapterForPojo_thenSuccess() {
    Gson gson = new GsonBuilder()
      .registerTypeAdapter(ConferencePojo.class, new ConferencePojoAdapter())
      .create();

    String json = """
        {
            "name": "Java Conference",
            "numberOfParticipants": 100
        }
        """;

    ConferencePojo result = gson.fromJson(json, ConferencePojo.class);

    assertNotNull(result);
    assertEquals("Java Conference", result.getName());
}

Note that we need to register the adapter with GsonBuilder.

5.4. The –add-opens Argument as a Last Resort

When we can’t modify the module’s source code, we still have one option left. We can open the package when the program starts by passing the –add-opens argument to the JVM. Let’s look at the argument for our gson-module project, which doesn’t open its package:

--add-opens gson.exception/gson.exception=com.google.gson

Starting from the left, we have the module name gson.exception, as declared in the module-info.java file. Next comes the package we want to open, gson.exception. Finally, we name the module we grant the access to, com.google.gson.

This is the launch-time equivalent of the opens statement, and with it, GsonModuleMain from gson-module deserializes the POJO successfully. However, we should treat it as a last resort, since the argument lives outside the code and we have to repeat it in every launch configuration. Our article on illegal reflective access covers the background in more detail.

6. Gson and Time

Besides problems with modular projects, we can run into the same exception when deserializing JSON into the date and time classes from the java.time API, such as LocalDate, Instant, or Duration. With strong encapsulation and an older Gson version, we get a JsonIOException with the message “Failed making field ‘java.time.LocalDate#year’ accessible”, again caused by an InaccessibleObjectException. This time, Gson tries to reach the private fields of a Java internal class. Moreover, we can’t solve this problem with the opens statement, as it can only open our own packages, not Java’s internals. However, Gson 2.14.0 has restored support for these classes.

6.1. LocalDate and Structured JSON

Let’s add a conferenceStart field in a new ConferencePojoWithDate class. We’re continuing in the gson-module-opens project, as the other fields are private:

public class ConferencePojoWithDate {

    private String name;
    private int numberOfParticipants;
    private LocalDate conferenceStart;

    // standard setters and getters
}

Now let’s prepare the JSON. We need to use a nested structure for conferenceStart:

{
    "name": "Java Conference",
    "numberOfParticipants": 500,
    "conferenceStart": {
        "year": 2026,
        "month": 8,
        "day": 17
    }
}

Note that the keys year, month, and day exactly match the names of the private fields of LocalDate. The same is true for the other time classes. For example, for Duration, the nested JSON provides the seconds and nanoseconds and looks like this:

"duration": {
    "seconds": 7200,
    "nanos": 0
}

Gson kept this naming convention for backward compatibility. It doesn’t (and isn’t allowed to) access the private fields of Java’s internal classes anymore. Instead, the authors implemented built-in TypeAdapters that use the public java.time API to transfer the data.

We should be especially careful to use the correct field names. If Gson doesn’t find a matching key in the JSON, it silently falls back to the default value of zero. For LocalDate, a zero month then triggers a completely different exception, a DateTimeException from the date validation in java.time.

Next, we can’t deserialize a date in ISO format out of the box. So, a plain string value such as the following fails with a JsonSyntaxException:

"conferenceStart":"2026-08-17"

To handle dates encoded as strings, we need to implement a custom TypeAdapter.

Finally, let’s emphasize that the same rules apply to records: we can deserialize nested time structures out of the box, and we need a TypeAdapter otherwise.

7. Conclusion

In this article, we looked at using Gson in modern, modular Java. We saw that it no longer works as easily as it did with Java 8 and the classpath.

We examined the problem in a modular project and looked at several ways to make Gson fit the rules of the JPMS. As our first measure, we made the class fields public. Next, we gave Gson access to private fields with the opens statement, or with the equivalent –add-opens JVM argument.

We then implemented a TypeAdapter that creates the object through its public API. We also saw that Gson works with records out of the box, using their canonical constructors.

Finally, we checked how Gson handles the classes from the java.time package. We noted that the newest version can deserialize nested time objects as expected again.

The bottom line is that we have two main ways to avoid the InaccessibleObjectException. The first is to open our code to Gson’s reflection, and with the opens statement, we can grant that access to Gson only. The second is to switch to records and stay within the rules of the JPMS. In addition, if we have a public API for creating the object, we can implement a TypeAdapter.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)
guest
0 Comments
Oldest
Newest