简体   繁体   English

当从json知道类型时,Gson解析通用类型

[英]Gson to parse generic type, when type is known from json

I have jsons that contain a message of any type and the json contains a String that says which type the message has. 我有一个json,其中包含任何类型的消息,并且json包含一个String,该字符串说明消息具有的类型。

I want to deserialize them and get 1. an Instance of the messag type representing the message and 2. an instance of Topic where T is the message type. 我想反序列化它们并得到1.一个代表消息的messag类型的实例,以及2.一个T为消息类型的Topic实例。

As examples: 例如:

input1 输入1

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

I expect a result after the deserialization to be the same as this done by hand: 我希望反序列化后的结果与手工完成的结果相同:

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

input2 输入2

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

I expect a result after the deserialization to be the same as this done by hand: 我希望反序列化后的结果与手工完成的结果相同:

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

input3 输入3

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

I expect a result after the deserialization to be the same as this done by hand: 我希望反序列化后的结果与手工完成的结果相同:

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

input4 输入4

... same with other types ...

I think you got the point. 我想你明白了。 But now I need to do this somehow in a generic/abstract way. 但是现在我需要以某种通用/抽象的方式进行此操作。 I tried this, but this will not work: 我试过了,但这行不通:

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 ?????

I don't know what to do?! 我不知道该怎么办?! I need Topic and Message typed, but how??? 我需要输入主题和消息,但是如何? The following seems so bad: 以下内容看起来很糟糕:

@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");
  }
}

That works for me now: 现在对我有用:

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

parse result data struct: 解析结果数据结构:

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

deserializer: 解串器:

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

usage: 用法:

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

In fact, to make it more dynamic I use a topics-manager which I pass to the deserializer in its constructor instead of a static map. 实际上,为了使其更具动态性,我使用了主题管理器,该主题管理器将传递给其构造函数中的反序列化器,而不是静态映射。 So I can register and unregister topic/message-types on the fly during runtime. 因此,我可以在运行时动态注册和取消注册主题/消息类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM