2017-01-27 22 views
1

Я новичок в gson и пытаюсь разобраться, как десериализировать список внутри объекта.gson - десериализация объектов, содержащих списки

Сообщение об ошибке указывает на создание экземпляра для игрока, который я сделал. Но когда это было реализовано, я обнаружил, что десериализованный объект содержит список игроков с полем имени, установленным на «значение по умолчанию», а не на сбор значений из строки 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

Список Try игроков = новый ArrayList <>(); вместо списка players = new ArrayList <>(); в TeamImpl, потому что ошибка интерфейса не может быть создана! Имя интерфейса: com.example.Player – Milaci

ответ

0

Gson работает с POJOs и не интерфейсов во время десериализации. Пожалуйста, используйте реализацию PlayerImpl вместо интерфейса Player. Существуют и другие библиотеки, такие как Genson, которые поддерживают требуемое вами требование.

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

} 

`

2

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 могут быть легко преобразованы в тривиальные реализации и легко скомпилированы из них.

0

Другие ответы почти правильно, но пропустили тот факт, что вы предоставили класс вместо интерфейса fromJson метода:

Team t2 = gson.fromJson(json, TeamImpl.class); 

Проблема здесь состоит в том, что у вас есть конструктор не по умолчанию (не арг), поэтому вызов класса # newInstance это невозможно.

Вы должны предоставить пользовательские десериализатор к GSON, это уже есть ответ here