2017-02-17 22 views
0

Чтобы начать, я просмотрел несколько других answers for similar questions, но они не отвечают на мою конкретную ситуацию.GSON Сериализуйте полиморфный объект с типом, хранящимся в другом объекте

Я разбор JSON сообщений, которые состоят из тела и заголовка, где хранит заголовок какого типа объекта тела является:

{ 
    "body": { 
     "eventName": "someEventName" 
    }, 
    "header": { 
     "purpose": "event" 
    } 
} 

В Java я моделируется эта структура, используя следующий классы:

public class Message { 
    public Body body; 
    public Header header; 
} 

public class Header { 
    public String purpose; // Marks what child class the body of the message uses 
} 

public abstract class Body { 
    // Child classes store additional fields 
} 

// Example implementation of the body class 
public class EventBody extends Body { 
    public String eventName; // Name of some event 
} 

После выполнения некоторых исследований я обнаружил, что RuntimeTypeAdapterFactory обычно используется для анализа/записи полиморфных объектов; однако класс RutimeTypeAdapterFactory полагается на тип, который хранится в базовом классе полиморфного объекта (т. е. Body). Но в этом случае это не так - тип сохраняется в другом объекте, Header.

Что было бы лучшим способом разобрать такие объекты? Я бы хотел, чтобы вам не пришлось писать обычай Serializer/Deserializer для компактности, но я бы не прочь написать их, если это необходимо.

+0

Тело является родительским классом ... –

+1

@JawadLeWywadi Да ... Я никогда не говорил, что это не так. Комментарий в 'Body' должен был упомянуть, что дочерние классы Body могут иметь дополнительные поля, _not_ это тело является дочерним классом. – jocopa3

ответ

2

Я понимаю, что прошение о решении, которое не связано с пользовательским Serializer/Deserializer, немного смешно, так как это именно тот тип сценария, в котором они будут использоваться (я думал, что могу уйти с пользовательский TypeAdapterFactory, но с помощью сериализатора/десериализатора проще).

В любом случае, для моего сценария комбинация пользовательского Сериализатора/Deserializer для класса Message работает нормально. Поскольку я уже использую перечисление для отслеживания различных целей сообщений и их имен строк, я решил просто добавить дополнительное поле в это перечисление, чтобы сохранить соответствующий класс тела.

MessagePurpose Enum:

public enum MessagePurpose { 
    EVENT("event", EventBody.class); 

    public final String purposeName; 
    public final Class bodyClass; 

    MessagePurpose(String purposeName, Class classi) { 
     this.purposeName = purposeName; 
     bodyClass = classi; 
    } 
} 

MessageSerializer:

public class MessageSerializer implements JsonSerializer<Message> { 
    @Override 
    public JsonElement serialize(Message message, Type type, JsonSerializationContext jsc) { 
     if(message == null) { 
      return null; 
     } 

     JsonObject messageObj = new JsonObject(); 

     // Get the class representing the body object from the purpose enum 
     Class bodyClassType = message.getPurpose().bodyClass; 
     messageObj.add("body", jsc.serialize(message.getBody(), bodyClassType)); 

     messageObj.add("header", jsc.serialize(message.getHeader(), Header.class)); 

     return messageObj; 
    } 
} 

MessageDeserializer:

public class MessageDeserializer implements JsonDeserializer<Message> { 
    @Override 
    public Message deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException { 
     Header header = jdc.deserialize(je.getAsJsonObject().get("header"), Header.class); 

     // Get the class representing the body object from the purpose enum 
     Class bodyClassType = header.getPurpose().bodyClass; 
     Body body = jdc.deserialize(je.getAsJsonObject().get("body"), bodyClassType); 

     return new Message(body, header); 
    } 
} 

Основная функция для тестирования с:

public static void main(String[] args) { 
    GsonBuilder gb = new GsonBuilder(); 

    // Register the Message class since I need to access info in the header 
    gb.registerTypeAdapter(Message.class, new MessageDeserializer()); 
    gb.registerTypeAdapter(Message.class, new MessageSerializer()); 

    Gson gson = gb.setPrettyPrinting().create(); 

    EventBody event = new EventBody(EventType.SOME_EVENT_NAME); 

    String eventJson = gson.toJson(event.getAsMessage()); 
    System.out.println(eventJson); 

    Message newEvent = gson.fromJson(eventJson); 
    System.out.println("\nEvent type: " + ((EventBody) newEvent.getBody()).getEventName()); 
} 

Вышеприведенные напечатало тестовый класс:

{ 
    "body": { 
    "eventType": "someEventName" 
    }, 
    "header": { 
    "purpose": "event" 
    } 
} 

Event Type: someEventName 

Этот вывод совпадает со JSON из сообщений я разборе, и это, кажется, десериализации различные типы сообщений, просто отлично.

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