我正在尝试用谷歌 Gson 库序列化一个类。
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
使用 java18-corretto
我认为谷歌 Gson lib 中存在问题。
你可以尝试改变:
public class XYZ {
private String name;
private LocalDateTime timeCreated;
}
致:
public class XYZ {
public String name;
public LocalDateTime timeCreated;
}
我已经在我的案例中这样做了,而且效果很好!
Instant
,不是LocalDateTime
实际上,
LocalDateTime
是用于跟踪“创建时”值的错误类。该类缺少时区或与 UTC 的偏移量的上下文,因此它不能表示时刻,即时间轴上的特定点。 LocalDateTime
本质上是模棱两可的。
而是使用
Instant
类。这个类代表了一个时刻,与 UTC 的偏移量为零小时-分钟-秒。
我不经常使用 Gson,所以我可能错了,但是......很奇怪 Gson 显然仍然缺乏对 java.time 的支持。尽管 JSR 310 多年前就被采用,早在 Java 8 中。
为了解决 Gson 中的不足,您需要导入一些提供 java.time 类型的库,或者您可以轻松 👉🏾 编写自己的适配器。
让我们从
Person
课开始。
注意两个成员字段
name
和createdAt
如何标记private
,就像你的问题中的字段一样。
还要注意这个类是如何不可变的,有 getter 但没有 setter。 Gson 仍然可以通过 Java Reflection 的魔力重构这个类的对象。这也是为什么您也可以将此类定义为短的单行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 + ']';
}
}
我们为
Instant
类型写了一个适配器。我们使用标准 ISO 8601 格式作为序列化值。这种格式化字符串末尾的 Z
是 +00:00
的缩写,表示与 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.
}
}
我们写了一个应用程序来展示那些正在行动的人。
注意我们如何为
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]