Gson java.time.LocalDateTime序列化错误

wdebmtf2  于 2023-03-29  发布在  Java
关注(0)|答案(2)|浏览(293)

我正在尝试使用Google Gson Library序列化一个类。

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

当我尝试执行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

使用java 18-corretto
我认为谷歌Gson lib有一个问题。

lpwwtiir

lpwwtiir1#

Instant,不是LocalDateTime

实际上,LocalDateTime错误的类,不适合用于跟踪“创建时”的值。该类缺少时区或UTC偏移量的上下文,因此它不能表示时刻,时间轴上的特定点。LocalDateTime本质上是不明确的。
相反,使用Instant类。该类表示从UTC偏移0小时-分钟-秒的时刻。

Gson适配器

我不经常使用Gson,所以我可能搞错了,但是...显然Gson仍然不支持 java.time。尽管JSR 310在几年前就被采用了,早在Java 8中。
要解决Gson中的不足,您需要导入一些提供 java.time 类型的库,或者您可以轻松地👉🏾编写自己的适配器。
让我们从一个Person类开始。
注意两个成员字段namecreatedAt是如何标记为private的,就像您的Question中的字段一样。
还要注意这个类是不可变的,有getter但没有setter。Gson仍然能够通过Java Reflection的魔力重新构造这个类的对象。这也是为什么你可以把这个类定义为一个简短的单行recordpublic 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 + ']';
    }
}

我们为Instant类型编写一个适配器。我们对序列化的值使用标准的ISO 8601。这样一个格式化字符串末尾的Z+00:00的缩写,意思是从UTC偏移0小时-分钟-秒。

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.
    }
}

我们写了一个应用程序来展示这些活动。
注意我们是如何为Instant类型注册适配器的。

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 );
    }
}

运行时:

personAsJson = {"name":"John Doe","createdAt":"2023-03-28T06:56:18.867974Z"}
p.toString() = Person[name=John Doe, createdAt=2023-03-28T06:56:18.867974Z]
wztqucjr

wztqucjr2#

您可以尝试更改:

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

收件人:

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

我已经这样做,在我的情况下,工作很好!

相关问题