0

I am trying to serialize a class with google Gson Library.

public class XYZ {
    private String name;
    private LocalDateTime timeCreated;
}

I getting an error when I try to execute return gson.toJson(this.obj);

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.time.LocalDate java.time.LocalDateTime.date accessible: module java.base does not "opens java.time" to unnamed module @1d8bd0de

using java18-corretto

I think there is an issue in google Gson lib.

Amsal N
  • 201
  • 2
  • 10
  • Maybe try the solution specified in this article : https://stackoverflow.com/questions/70412805/what-does-this-error-mean-java-lang-reflect-inaccessibleobjectexception-unable – Avishek Bhattacharya Mar 28 '23 at 04:50
  • By the way, `LocalDateTime` is the wrong class to use for tracking a "when created" value. That class lacks the context of a time zone or offset-from-UTC, so it cannot represent a moment, a specific point on the timeline. Instead, use the `Instant` class. – Basil Bourque Mar 28 '23 at 06:40

2 Answers2

2

Instant, not LocalDateTime

Actually, LocalDateTime is the wrong class to use for tracking a "when created" value. That class lacks the context of a time zone or offset-from-UTC, so it cannot represent a moment, a specific point on the timeline. A LocalDateTime is inherently ambiguous.

Instead, use the Instant class. This class represents a moment as seen with an offset from UTC of zero hours-minutes-seconds.

Gson adapter

I do not regularly use Gson, so I may be mistaken, but… Apparently Gson oddly still lacks support for java.time. This despite JSR 310 being adopted years ago, way back in Java 8.

To get around that lacking in Gson, You will need to either import some library that provides java.time types, or you can easily write your own adapter.

Let's start with a Person class.

Notice how the two member fields name & createdAt are marked private, as are the fields in your Question.

Also notice how this class is immutable, with getters but no setters. Gson is still able to reconstitute an object of this class via the magic of Java Reflection. This is also why you could just as well define this class as a short single-line record: public record Person ( String name , Instant createdAt ) {}.

package work.basil.example.gson;

import java.time.Instant;
import java.util.Objects;

public final class Person
{
    private final String name;
    private final Instant createdAt;

    public Person ( String name , Instant createdAt )
    {
        this.name = name;
        this.createdAt = createdAt;
    }

    public String name ( ) { return name; }

    public Instant createdAt ( ) { return createdAt; }

    @Override
    public boolean equals ( Object obj )
    {
        if ( obj == this ) { return true; }
        if ( obj == null || obj.getClass() != this.getClass() ) { return false; }
        var that = ( Person ) obj;
        return Objects.equals( this.name , that.name ) &&
               Objects.equals( this.createdAt , that.createdAt );
    }

    @Override
    public int hashCode ( )
    {
        return Objects.hash( name , createdAt );
    }

    @Override
    public String toString ( )
    {
        return "Person[" +
               "name=" + name + ", " +
               "createdAt=" + createdAt + ']';
    }
}

We write an adapter for the Instant type. We use standard ISO 8601 format for the serialized value. The Z on the end of such a formatted string is an abbreviation of +00:00, meaning an offset of zero hours-minutes-seconds from UTC.

package work.basil.example.gson;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.time.Instant;

public class Gson_InstantTypeAdapter extends TypeAdapter < Instant >
{
    @Override
    public void write ( JsonWriter jsonWriter , Instant instant ) throws IOException
    {
        jsonWriter.value( instant.toString() );  // Writes in standard ISO 8601 format.
    }

    @Override
    public Instant read ( JsonReader jsonReader ) throws IOException
    {
        return Instant.parse( jsonReader.nextString() );   // Parses standard ISO 8601 format.
    }
}

And we write an app to show those in action.

Notice how we register our adapter for the Instant type.

package work.basil.example.gson;

import com.google.gson.*;

import java.time.Instant;

public class App
{
    public static void main ( String[] args )
    {
        App app = new App();
        app.demo();
    }

    private void demo ( )
    {
        Gson gson =
                new GsonBuilder()
                        .registerTypeAdapter( Instant.class , new Gson_InstantTypeAdapter() )
                        .create();

        // Write to JSON.
        Person person = new Person( "John Doe" , Instant.now() ); //For example: 2022-02-02T11:11:510Z
        String personAsJson = gson.toJson( person );

        System.out.println( "personAsJson = " + personAsJson );

        // Parse JSON.
        Person p = gson.fromJson( personAsJson , Person.class );

        System.out.println( "p.toString() = " + p );
    }
}

When run:

personAsJson = {"name":"John Doe","createdAt":"2023-03-28T06:56:18.867974Z"}
p.toString() = Person[name=John Doe, createdAt=2023-03-28T06:56:18.867974Z]
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0

You can try Change:

public class XYZ {
    private String name;
    private LocalDateTime timeCreated;
}

To:

public class XYZ {
    public String name;
    public LocalDateTime timeCreated;
}

I haved do this in my case and that work fine !