我是gson的新手,正在尝试解决如何对对象中的列表进行反序列化。
错误消息提示创建InstanceCreator for Player,我这样做了。 但是在实现时,我发现反序列化对象包含一个名称字段设置为“默认值”的玩家列表,而不是从json字符串中获取值。所以我现在想知道这种方法是否正确。
这是我正在处理的模型的简化,但突出了问题......
public interface Player {
String name();
}
public class PlayerImpl implements Player {
private String name;
public PlayerImpl(String name) {
this.name = name;
}
public String name() { return this.name; }
}
public interface Team {
...
}
public class TeamImpl implements Team {
String name;
List<Player> players = new ArrayList<>();
public TeamImpl(String teamName) { this.name = teamName; }
...
}
我有一个简单的测试来创建一个有2个玩家的新团队
Team t = new TeamImpl("teamname");
t.addPlayer(new PlayerImpl("p1"));
t.addPlayer(new PlayerImpl("p2"));
Gson gson = new Gson();
String json = gson.toJson(t);
创建以下json字符串:
{"name":"teamname","players":[{"name":"p1"},{"name":"p2"}]}
然而,当我反序列化json字符串...
Team t2 = gson.fromJson(json, TeamImpl.class);
我收到以下错误:
java.lang.RuntimeException: Unable to invoke no-args constructor for interface com.example.Player. Register an InstanceCreator with Gson for this type may fix this problem.
at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:226)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:210)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:129)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:220)
at com.google.gson.Gson.fromJson(Gson.java:887)
at com.google.gson.Gson.fromJson(Gson.java:852)
at com.google.gson.Gson.fromJson(Gson.java:801)
at com.google.gson.Gson.fromJson(Gson.java:773)
at com.example.data.JsonDataTests.test_team_json(JsonDataTests.java:62)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99)
at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81)
at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75)
at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45)
at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:71)
at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35)
at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42)
at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52)
at org.junit.runner.JUnitCore.run(JUnitCore.java:121)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.UnsupportedOperationException: Interface can't be instantiated! Interface name: com.example.Player
at com.google.gson.internal.UnsafeAllocator.assertInstantiable(UnsafeAllocator.java:117)
at com.google.gson.internal.UnsafeAllocator.access$000(UnsafeAllocator.java:31)
at com.google.gson.internal.UnsafeAllocator$1.newInstance(UnsafeAllocator.java:49)
at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:223)
... 35 more
答案 0 :(得分:4)
Gson清楚地报告它无法实例化接口。为了使其成为可能,您可以注册一个自定义类型适配器,该适配器将知道如何序列化实例并将其反序列化。
final class InterfaceSerializer<T>
implements JsonSerializer<T>, JsonDeserializer<T> {
private final Class<T> implementationClass;
private InterfaceSerializer(final Class<T> implementationClass) {
this.implementationClass = implementationClass;
}
static <T> InterfaceSerializer<T> interfaceSerializer(final Class<T> implementationClass) {
return new InterfaceSerializer<>(implementationClass);
}
@Override
public JsonElement serialize(final T value, final Type type, final JsonSerializationContext context) {
final Type targetType = value != null
? value.getClass() // `type` can be an interface so Gson would not even try to traverse the fields, just pick the implementation class
: type; // if not, then delegate further
return context.serialize(value, targetType);
}
@Override
public T deserialize(final JsonElement jsonElement, final Type typeOfT, final JsonDeserializationContext context) {
return context.deserialize(jsonElement, implementationClass);
}
}
然后配置你的Gson实例(每个应用程序一次,Gson是不可变的和线程安全的):
Gson gson = new GsonBuilder()
.registerTypeAdapter(Player.class, interfaceSerializer(PlayerImpl.class))
.registerTypeAdapter(Team.class, interfaceSerializer(TeamImpl.class))
.create();
请注意,现在即使Team t2 = gson.fromJson(json, Team.class);
也可以使用(不是TeamImpl.class
。
也许基于意见,但是......我强烈建议你不要绑定Gson中的接口。 Gson唯一的责任是对您的应用程序使用的业务/价值对象进行序列化和反序列化。查看描述问题的Data Transfer Object模式,并建议对与应用程序进行传输数据的对象进行分段。有了这个,您可以从DTO中删除接口,这样您就不需要这样的适配器,也不关心如何声明和注释DTO类。知道您可能有TeamDto
和PlayerDto
甚至无法实现您的接口,但清楚地绑定数据,比如class TeamDto { private List<PlayerDto> }
。 DTO可以很容易地转换为普通的实现,也可以从它们中轻松构建。
答案 1 :(得分:0)
Gson在反序列化期间使用POJO而不是接口。请使用实施PlayerImpl
而不是接口Player
。还有其他库,如Genson,它们支持您的要求。
答案 2 :(得分:0)
你有这个错误:Caused by: java.lang.UnsupportedOperationException: Interface can't be instantiated! Interface name: com.example.Player
所以你应该在TeamImpl
里面建一个列表,像这样`List players = new ArrayList();因为无法在GSON中实例化接口。
public interface Player {
String name();
}
public class PlayerImpl implements Player {
private String name;
public PlayerImpl(String name) {
this.name = name;
}
public String name() { return this.name; }
}
public interface Team {
void addPlayer(PlayerImpl p1);
}
import java.util.ArrayList;
import java.util.List;
public class TeamImpl implements Team {
String name;
List<PlayerImpl> players = new ArrayList<PlayerImpl>();
public TeamImpl(String teamName) { this.name = teamName; }
@Override
public void addPlayer(PlayerImpl p1) {
this.players.add(p1);
}
}
And the main to test it:
public static void main(String[] args) {
Team t = new TeamImpl("teamname");
t.addPlayer(new PlayerImpl("p1"));
t.addPlayer(new PlayerImpl("p2"));
Gson gson = new Gson();
String json = gson.toJson(t);
System.out.println(json);
Team t2 = gson.fromJson(json, TeamImpl.class);
System.out.println(t2.toString());
}
`
答案 3 :(得分:0)
其他答案几乎是正确的,但错过了您提供类而不是fromJson
方法的接口的事实:
Team t2 = gson.fromJson(json, TeamImpl.class);
这里的问题是你没有默认构造函数(没有args),因此无法调用Class#newInstance。
您需要向GSON提供自定义反序列化器,已经回答here
答案 4 :(得分:0)
通常,当类使用接口作为字段时,也会引起此异常。尤其是在创建树结构时,很难发现自己在做错什么:
class A implements IA{
IA parent;
Collection<IA> nodes;
}
IA myimpl = gson.fromJson( data_str, A.class );
其中IA是类A的接口。此代码将引发上面的异常。