2014-09-24 5 views
3

Я использую gson для преобразования java-объектов в объекты json и из них. Я столкнулся с проблемой полиморфизма.gson - Пользовательский десериализатор для полиморфных переменных

У меня есть эти запросы, которые выглядят примерно так:

{ 
    "method": "getUser", 
    "methodParameters": { 
    "a": "b", 
    "c": "d", 
    "e": "f", 
    "data": { 
     "u": "v", 
     "x": "y" 
    } 
    }, 
    "version": "1.3" 
} 

Каждого метод запрос имеет другой тип объекта данных. Естественно, каждый объект данных расширяет базовый класс, называемый RequestData.

Я попытался создать пользовательский десериализатор, но поскольку это объект запроса, а не объект данных, который поддерживает этот метод, я не мог найти способ узнать, какой объект для десериализации.

Возможно ли каким-либо образом получить значение метода при десериализации объекта данных или есть ли другой лучший способ решить эту проблему? Благодарю.

ответ

2

Я столкнулся с аналогичной проблемой: как указал Томек, вам понадобится специальный десериализатор, а также определенное поле JSON, которое вы будете использовать во время выполнения, чтобы решить, какой подкласс должен иметь экземпляр.

Рассмотрим следующий класс в качестве базового класса:

// Base class for a server sent event 
public class ServerEvent 
{ 
    public static final String TYPEFIELDNAME = "eventType"; 

    // Event type is used during deserialization to choose the right subclass 
    private final String eventType; 

    public ServerEvent(String eventType) 
    { 
     this.eventType = eventType; 
    } 

    public String toString() 
    { 
     return "eventType: " + eventType; 
    } 
} 

ServerEvent имеет два подкласса, каждый из которых с различными свойствами:

public class EventA extends ServerEvent 
{ 
    private static final String EVENTTYPE = "eventa"; 
    private int data; 

    public EventA() 
    { 
     super(EVENTTYPE); 
    } 

    // Helper function to register this class with ServerEventDeserializer 
    public static void register(ServerEventDeserializer deserializer) 
    { 
     deserializer.registerEvent(EVENTTYPE, EventA.class); 
    } 

    public String toString() 
    { 
     return super.toString() + ", data = " + data; 
    } 
} 

public class EventB extends ServerEvent 
{ 
    private static final String EVENTTYPE = "eventb"; 
    private String name; 

    public EventB() 
    { 
     super(EVENTTYPE); 
    } 

    // Helper function to register this class with ServerEventDeserializer 
    public static void register(ServerEventDeserializer deserializer) 
    { 
     deserializer.registerEvent(EVENTTYPE, EventB.class); 
    } 

    public String toString() 
    { 
     return super.toString() + ", name = " + name; 
    } 
} 

Два возможных входов могут быть следующие:

{ "eventType" : "eventa", "data" : 15 } 
{ "eventType" : "eventb", "name" : "test" } 

Это полиморфный десериализатор:

// This class handles the polymorphic deserialization of ServerEvent class 
public class ServerEventDeserializer implements JsonDeserializer<ServerEvent> 
{ 
    // Gson engine 
    private Gson gson; 

    // Map of subclasses 
    private Map<String, Class<? extends ServerEvent>> eventRegistry; 

    public ServerEventDeserializer() 
    { 
     gson = new Gson(); 
     eventRegistry = new HashMap<String, Class<? extends ServerEvent>>(); 
    } 

    // Registers a ServerEvent subclass 
    public void registerEvent(String event, Class<? extends ServerEvent> eventInstanceClass) 
    { 
     eventRegistry.put(event, eventInstanceClass); 
    } 

    @Override 
    public ServerEvent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException 
    { 
     try 
     { 
      // Get the JSON object 
      JsonObject commandObject = json.getAsJsonObject(); 

      // Read the field named "ServerEvent.TYPEFIELDNAME" 
      JsonElement commandTypeElement = commandObject.get(ServerEvent.TYPEFIELDNAME); 

      // Query the eventRegistry map to instance the right subclass 
      Class<? extends ServerEvent> commandInstanceClass = eventRegistry.get(commandTypeElement.getAsString()); 
      ServerEvent command = gson.fromJson(json, commandInstanceClass); 

      return command; 
     } 
     catch (Exception e) 
     { 
      throw new RuntimeException(e); 
     } 
    } 
} 

Я написал минимальный рабочий пример, который можно загрузить here.

0

Когда вы десериализация этого примера JSON строка пользовательского десериализатора будет иметь метод заголовок так:

public YOUR_CLASS deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 

(добавить этот десериализатор по registerTypeAdapter() метод в объекте GsonBuilder)

Вы можете конвертировать JsonElement в string, извлеките свой методParameters и на основе чего-то типичного для каждого подкласса RequestData, вы можете создать конкретный класс. Скажем, например, что у нас есть MyRequestData, этот класс имеет 10 параметров. Я проверяю строку json, и если у нее так много параметров, возвращаем этот конкретный экземпляр. Конечно, сначала вы можете десериализовать методParameters (в классе десериализатора) на класс Map, а затем проанализировать его, чтобы выбрать правильный класс.

+0

Спасибо за ответ! Я не могу посмотреть на количество параметров, поскольку два метода могут иметь одинаковое количество параметров. Они даже должны иметь точно такие же параметры. Можете ли вы разработать десериализацию методов? – anony115511

+0

Хорошо позже. Но у меня вопрос, в чем разница между этими классами? Только реализация определенных методов? В таком случае их невозможно распознать. Затем вы можете просто добавить дополнительный параметр, например «имя» и в имя набора конструктора вашего конкретного класса. Это будет самое простое решение. Тогда десериализация будет легкой. – Tomek

Смежные вопросы