【问题标题】:Gson can't deserialize inherited class?Gson 不能反序列化继承的类?
【发布时间】:2013-04-25 16:25:29
【问题描述】:

我有一个简单的 Json 结构,例如:

{"MessageType":"TimeData","TimeData":{"hh":12,"mm":13,"ms":15,"ss":14}}

我设计了以下类来反序列化它:

public class JsonMessage
{
    public enum MessageTypes{
        WhoAreYou,
        TimeData
    }
    JsonMessage(){
    }
    public MessageTypes MessageType;
}
class TimeData extends JsonMessage{
    int hh;
    int mm;
    int ss;
    int ms;

    TimeData() {
    }    
}

我需要将反序列化分成两个阶段:

1- 反序列化以读取MessageType

2- 基于MessageType 进行其余的反序列化

代码很简单:

public void dispatch(Object message, IoSession session)
    {

            Gson gson = new Gson();
            JsonMessage result = gson.fromJson(message.toString(), JsonMessage.class);
            System.out.println(result.MessageType.toString());
            switch (result.MessageType)
                {
                    case WhoAreYou:{
                    //.....
                    break;
                    }
                    case TimeUpdate:
                        TimeData res = new Gson().fromJson(message.toString(), TimeData.class);
                        System.out.println(res.hh);
                        break;
                    default:break;
        }
    }

我的程序可以输入正确的switch-case(即TimeUpdate),但解析不正确(println 打印的是 0 而不是 12)

你认为我哪里做错了? 谢谢

【问题讨论】:

标签: java json gson


【解决方案1】:

问题是您的 JSON 代表一个包含您感兴趣的另一个对象的对象,而您的 Java 只是一个对象。

实际上,您可以为每种类型编写反序列化程序,并在确定 MessageType 后使用它们:

public static void main(String[] args)
{
    Gson gson = new GsonBuilder().registerTypeAdapter(TimeData.class, new TimeDataDeserializer()).create();
    String json = "{\"MessageType\":\"TimeData\",\"TimeData\":{\"hh\":12,\"mm\":13,\"ms\":15,\"ss\":14}}";
    JsonMessage message = gson.fromJson(json, JsonMessage.class);

    switch(message.MessageType)
    {
        case TimeData:
            TimeData td = new GsonBuilder()
                            .registerTypeAdapter(TimeData.class, new TimeDataDeserializer())
                            .create()
                            .fromJson(json, TimeData.class);
            td.MessageType = message.MessageType
            System.out.println(td.hh);
            break;
        default:
            break;
    }
}

class TimeDataDeserializer implements JsonDeserializer<TimeData>
{
    @Override
    public TimeData deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)  
        throws JsonParseException
    {
        JsonObject jo = je.getAsJsonObject().getAsJsonObject("TimeData");
        Gson g = new Gson();
        return g.fromJson(jo, TimeData.class);
    }
}

【讨论】:

  • 它仍然不会将整个响应转换为 TimeData 对象,因为 MessageTypes 是从父类而不是子类访问的。有什么可以解决这个问题的答案吗?
【解决方案2】:

我设法通过以下方式实现自定义JsonDeserializer 解决了这个类似的问题。

首先,您根据类型和方法将子类附加到您的枚举,以根据枚举名称检索正确的Class&lt;?&gt;

enum MessageType {
  WHO_ARE_YOU(WhoAreYou.class),
  TIME_UPDATE(TimeUpdate.class);

  public final Class<?> clazz;

  MessageType(Class<?> clazz) { this.clazz = clazz; }

  public static MessageType forName(String name) {
    for (MessageType t : values())
      if (name.equals(t.name()))
        return t;

    return NULL;
  }
}

然后在deserialize 方法中我做了以下操作:

public JsonMessage deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
   JsonObject object = json.getAsJsonObject();
   String kind = object.get("messageType").getAsString();
   Class<?> clazz = MessageType.forName(kind).clazz;
   JsonMessage result = null;

   try {
     result = (JsonMessage)clazz.newInstance();
     Field[] fs = clazz.getFields();

     for (Field f : fs) {
       Object value = context.deserialize(object.get(f.getName()), f.getType());                 
       if (value != null)
         f.set(result, value);
     }
   } 
   catch (Exception e) {
     e.printStackTrace();
   }
}

一切都通过反射进行管理,以便创建正确的对象,然后相应地反序列化所有字段。

我有一个复杂的对象层次结构,所以我更喜欢这样让 gson 反序列化器管理所有内容。当然,您需要使用 gson 解析器实例注册序列化程序。

注意:根据 Java 标准,您对事物的命名非常不正确。枚举常量应该是ALL_CAPITALIZED,枚举类名应该是单数(例如MessageType)。实例变量应该是驼峰式(例如messageType 而不是MessageType

【讨论】:

  • 感谢您的解决方案。几天前我开始Java编程,所以我需要阅读代码几次才能理解。我现在将开始进行更改,并会立即让您知道结果。谢谢
  • 杰克,对不起:在你的第一个代码 sn-p 中,我不得不将 for (MessageType t : values) 更改为 for (MessageType t : MessageType.values()),即使这样我也得到了错误:“无法引用非静态变量 MessageType从静态上下文中“我尝试了一点上下没有运气。你能帮忙吗?
  • 创建static 方法就完成了(是的,将values 替换为values()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多