【问题标题】:Gson to parse generic type, when type is known from json当从 json 中知道类型时,Gson 解析泛型类型
【发布时间】:2014-01-25 00:49:17
【问题描述】:

我的 json 包含任何类型的消息,并且 json 包含一个字符串,说明消息具有哪种类型。

我想反序列化它们并获得 1. 表示消息的消息类型的实例和 2. 主题的实例,其中 T 是消息类型。

举例:

输入1

{
"messageType":"String",
"message": "a string"
}

我希望反序列化后的结果与手动完成的结果相同:

Topic<String> t = new Topic<String>(String.class);
String message = "a string";

输入2

{
"messageType":"Integer",
"message": 1
}

我希望反序列化后的结果与手动完成的结果相同:

Topic<Integer> t = new Topic<Integer>(Integer.class);
Integer message = 1;

输入3

{
"messageType":"MyClass",
"message": {"a": "something", "b": 1}
}

我希望反序列化后的结果与手动完成的结果相同:

Topic<MyClass> t = new Topic<MyClass>(MyClass.class);
MyClass message = new MyClass("something", 1);

输入4

... same with other types ...

我想你明白了。但是现在我需要以通用/抽象的方式以某种方式做到这一点。我试过了,但这不起作用:

private enum MessageType {
  STRING(String.class), INTEGER(Integer.class), BOOLEAN(Boolean.class), MYCLASS(MyClass.class);
  private Class<?> clazz;
  MessageType(Class<?> clazz) {
    this.clazz = clazz;
  }
}


private static class MyJson {
  String topicId;
  String messageType;
  Object message;
}


MyJson<?> myJson = gson.fromJson(input, MyJson.class);
MessageType type = MessageType.valueOf(myJson.messageType);
Class<?> clazz = type.getClass();

??? message = clazz.newInstance(message);
Topic<???> t = new ?????

我不知道该怎么办?!我需要输入主题和消息,但是如何?以下似乎很糟糕:

@SuppressWarnings("unchecked")
private <T> Topic<T> createTopic(Class<T> typeClass) {
  try {
    return Topic.class.getConstructor(typeClass).newInstance(typeClass);
  }
  catch (Exception e) {
    e.printStackTrace();
    throw new RuntimeException("Fail");
  }
}

【问题讨论】:

  • 这正是你所做的。泛型是 Java 中的编译时特性,当您试图从运行时到达的字符串中找出类型时,它们绝对不会为您做任何事情。看起来好像可以的图书馆只是在做你已经到达的引擎盖下的同样不安全的事情。
  • 是的,但我现在该怎么做?我的方法不起作用
  • 我需要消息类型,但如何获取?
  • 如果所有的 possible 值在编译时都是已知的,您可以创建 TypeToken 并让 GSON 使用类型令牌反序列化“消息”的内容,然后将其设置在您的主题。
  • Affe,但是我仍然需要将 messageType 与 typetoken 匹配。或者有没有可能是gson自己猜到了呢?

标签: java json generics serialization gson


【解决方案1】:

现在对我有用:

class Topic<MessageType> {
    // ...
    public Class<MessageType> getMessageTypeClass() // ...
    // ...
}

解析结果数据结构:

public class TopicLine {
  public Topic<?> topic;
  public Object message;
}

反序列化器:

public class MessageDeserializer implements JsonDeserializer<TopicLine> {
  private static class InternalParseLine {
    String messageType;
    JsonElement message;
  }

  private Map<String, Topic<?>> messageTypes = new HashMap<String, Topic<?>>();

  public MessageDeserializer() {
    messageTypes.put("Integer", new Topic<Integer>(Integer.class));
    // other topics
  }

  private static class InternalParseLine {
    String topicId;
    JsonElement message;
  }

  @Override
  public TopicLine deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

    InternalParseLine line = context.deserialize(json, InternalParseLine.class);

    TopicLine topicLine = new TopicLine();
    topicLine.topic = topics.get(line.messageType);
    topicLine.message = context.deserialize(line.message, topicLine.topic.getMessageTypeClass());
    return topicLine;
  }
}

用法:

json = new GsonBuilder()
    .registerTypeAdapter(TopicLine.class, new TopicLineDeserializer())
    .create();
TopicLine t = json.fromJson(line, TopicLine.class);

事实上,为了使其更具动态性,我使用了一个主题管理器,我在其构造函数中将其传递给反序列化器,而不是静态映射。所以我可以在运行时动态注册和注销主题/消息类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-02
    • 2014-02-04
    • 1970-01-01
    相关资源
    最近更新 更多