【问题标题】:GSON Serialize Polymorphic Object with Type Stored in a Different ObjectGSON 序列化类型存储在不同对象中的多态对象
【发布时间】:2017-07-07 15:35:15
【问题描述】:

首先,我查看了其他几个answers for similar questions,但他们没有回答我的具体情况。

我正在解析由正文和标头组成的 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,但如果有必要我不介意编写它们。

【问题讨论】:

  • Body 是父类...
  • @JawadLeWywadi 是的......我从来没有说过不是。 Body 中的评论是提到 Body 的子类可以有额外的字段,notbody 是子类。

标签: java json serialization polymorphism gson


【解决方案1】:

我意识到要求一个不涉及自定义 Serializer/Deserializer 的解决方案有点荒谬,因为这正是他们将用于的场景类型(我想我可以得到不用自定义 TypeAdapterFactory,但使用 Serializer/Deserializer 更容易)。

无论如何,对于我的场景,Message 类的自定义序列化器/反序列化器的组合似乎可以正常工作。由于我已经使用枚举来跟踪不同的消息用途及其字符串名称,因此我决定简单地向该枚举添加一个附加字段来存储相应的正文类。

MessagePurpose 枚举:

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;
    }
}

消息序列化器:

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;
    }
}

消息解串器:

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 匹配,并且它似乎可以很好地反序列化不同类型的消息。

【讨论】:

    猜你喜欢
    • 2023-04-09
    • 2016-01-11
    • 1970-01-01
    • 2015-07-27
    • 1970-01-01
    • 1970-01-01
    • 2020-12-25
    • 1970-01-01
    相关资源
    最近更新 更多