2015-10-17 5 views
2

Возможно, я бегу в неправильном направлении, но у меня есть список элементов, которые я хочу прочитать.Пользовательская сериализация gson для абстрактного класса

У меня есть абстрактный базовый класс, назовем его Person:

public abstract class Person { 
    public int id; 
    public String name; 
} 

Теперь у меня есть два возможных реализаций:

public class Hunter implements Person { 
    public int skill; 
    // and some more stuff 
} 

public class Zombie implements Person { 
    public int uglyness; 
    // and some more stuff 
} 

Теперь у меня есть этот пример JSON:

[ 
    {"id":1, "type":"zombie", "name":"Ugly Tom", "uglyness":42}, 
    {"id":2, "type":"hunter", "name":"Shoot in leg Joe", "skill":0} 
] 

Как я могу прочитать этот JSON как List<Person>?

Я играю какое-то время с TypeAdapterFactory и пытался использовать класс под названием CustomizedTypeAdapterFactory, так как моя реальная структура немного сложнее, чем забавный пример выше.

Я закончил тем, что я хочу, чтобы делегировать сериализации с этим вызовом:

return gson.getDelegateAdapter(this, resultType); 

Однако я понятия не имею, как я могу создать что во время выполнения TypeToken<T>, которое требуется для этого вызова. Есть идеи?

+0

Этот вопрос похож на http://stackoverflow.com/questions/16000163/using-gson-and-abstract-classes – bhspencer

ответ

3

Как я могу прочитать этот JSON как Список?

Одной из возможностей было бы создание индивидуального десериализатора, который действует как завод.

Первый шаг был бы определить эту десериализатор

class PersonJsonDeserializer implements JsonDeserializer<Person> { 
    @Override 
    public Person deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 
     String type = json.getAsJsonObject().get("type").getAsString(); 
     switch(type) { 
      case "zombie": 
       return context.deserialize(json, Zombie.class); 
      case "hunter": 
       return context.deserialize(json, Hunter.class); 
      default: 
       throw new IllegalArgumentException("Neither zombie or hunter"); 
     } 
    } 
} 

Он извлекает значение, связанное с ключом «типом» и выберите правильный тип для десериализации объекта, который вы сейчас читаете.

Затем вам необходимо подключить этот десериализатор в парсер.

public class GsonTest { 
    public static void main(String[] args) { 
     String json = "[\n" + 
       " {\"id\":1, \"type\":\"zombie\", \"name\":\"Ugly Tom\", \"uglyness\":42},\n" + 
       " {\"id\":2, \"type\":\"hunter\", \"name\":\"Shoot in leg Joe\", \"skill\":0}\n" + 
       "]"; 

     Gson gson = new GsonBuilder().registerTypeAdapter(Person.class, new PersonJsonDeserializer()).create(); 

     Type type = new TypeToken<List<Person>>(){}.getType(); 

     List<Person> list = gson.fromJson(json, type); 

     for(Person p : list) { 
      System.out.println(p); 
     } 
    } 
} 

Запуск его с вашим примером, я получаю:

Zombie{id=1; name=Ugly Tom; uglyness=42} 
Hunter{id=2; name=Shoot in leg Joe; skill=0} 

Если значение типа уже соответствует имени класса, вы можете захотеть использовать Class.forName также:

class PersonJsonDeserializer implements JsonDeserializer<Person> { 
    @Override 
    public Person deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 
     String className = json.getAsJsonObject().get("type").getAsString(); 
     className = Character.toUpperCase(className.charAt(0)) + className.substring(1); 
     try { 
      return context.deserialize(json, Class.forName(className)); 
     } catch (ClassNotFoundException e) { 
      throw new RuntimeException(e); 
     } 
    } 
} 
Смежные вопросы