【问题标题】:Deserialize recursive polymorphic class in GSON反序列化 GSON 中的递归多态类
【发布时间】:2023-03-03 09:28:27
【问题描述】:
class Complex implements Recursive {
  Map<String, Recursive> map;
  ...
}

class Simple implements Recursive { ... }

我如何反序列化这个 json:

{
  "type" : "complex",
  "map" : {
     "a" : {
        "type" : "simple"
     },
     "b" : {
        "type" : "complex",
        "map" : {
            "ba" : {
                "type" : "simple"
        } 
     } 
  }
}

使用谷歌 GSON?

【问题讨论】:

    标签: java json deserialization gson


    【解决方案1】:

    要反序列化您的 JSON,您需要一个用于递归接口的自定义反序列化器。在那种类中,您需要检查您的 JSON 并决定将哪种类实例化为 JSON 本身中的类型字段。在这里,您有一个我为您编写的基本反序列化器示例。

    当然可以改进管理边界情况(例如,如果您没有类型字段会怎样?)。

    package stackoverflow.questions;
    
    import java.lang.reflect.Type;
    import java.util.*;
    
    import stackoverflow.questions.Q20254329.*;
    
    import com.google.gson.*;
    import com.google.gson.reflect.TypeToken;
    
    public class Q20327670 {
    
       static class Complex implements Recursive {
          Map<String, Recursive> map;
    
          @Override
          public String toString() {
             return "Complex [map=" + map + "]";
          }
    
       }
    
       static class Simple implements Recursive {
    
          @Override
          public String toString() {
             return "Simple []";
          }
       }
    
       public static class RecursiveDeserializer implements JsonDeserializer<Recursive> {
    
          public Recursive deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
             Recursive r = null;
             if (json == null)
                r = null;
             else {
                JsonElement t = json.getAsJsonObject().get("type");
                String type = null;
                if (t != null) {
                   type = t.getAsString();
    
                   switch (type) {
                   case "complex": {
                      Complex c = new Complex();
                      JsonElement e = json.getAsJsonObject().get("map");
                      if (e != null) {
                         Type mapType = new TypeToken<Map<String, Recursive>>() {}.getType();
                         c.map = context.deserialize(e, mapType);
                      }
                      r = c;
                      break;
                   }
                   case "simple": {
                      r = new Simple();
                      break;
                   }
                   // remember to manage default..
                   }
    
                }
             }
             return r;
          }
    
       }
    
       public static void main(String[] args) {
          String json = " {                                         " + 
                        "    \"type\" : \"complex\",                " + 
                        "    \"map\" : {                            " + 
                        "       \"a\" : {                           " +
                        "          \"type\" : \"simple\"            " +
                        "       },                                  " + 
                        "       \"b\" : {                           " +
                        "          \"type\" : \"complex\",          " + 
                        "          \"map\" : {                      " + 
                        "              \"ba\" : {                   " +
                        "                  \"type\" : \"simple\"    " +
                        "          }                                " +
                        "       }                                   " +
                        "    }                                      " +
                        "  }  }                                     ";
    
          GsonBuilder gb = new GsonBuilder();
          gb.registerTypeAdapter(Recursive.class, new RecursiveDeserializer());
    
          Gson gson = gb.create();
          Recursive r = gson.fromJson(json, Recursive.class);
    
          System.out.println(r);
    
       }
    
    }
    

    这是我的代码的结果:

    Complex [map={a=Simple [], b=Complex [map={ba=Simple []}]}]
    

    【讨论】:

    • 嘿,谢谢,这可以工作。但是想象一下,我在ComplexSimple 类中都有更多的属性。然后我必须手动反序列化每个字段(..get("map");),这实际上失去了 GSON 给我的所有自动魔法。如果不是 2 个,而是 10 个子类怎么办?有什么想法吗?
    • 能否控制JSON?
    • 如果你可以把“类型”放在类序列化之外,它就可以了。我可以给你看,但不是现在。我想我可以在接下来的 24 小时内准备一个完整的示例
    • 这样可以吗? { type: "complex", instance: { map: { ... } } } 表示 type 与实际类字段不在同一级别(此处嵌套在 instance 元素内)?
    • 是的,你明白了。使答案反序列化器适应这种 JSON 应该很简单。无论如何,只有通过运行代码我才能确定这一点。
    【解决方案2】:

    抱歉回复太晚了(因为这个问题已经回答了)。但我想我可以为这个问题付出我的 2 美分。

    正如@Parobay 之前所说,您可以添加一个特殊的{"type": "complex"} 参数以了解您要反序列化哪个类。但如前所述,您必须将所有数据移动到 {"instance": {}} 子对象,这感觉很糟糕,因为您必须手动控制在大 switch 中创建的实例。

    您可以改用TypeFactory 类来更好地处理这种情况。更准确地说,有一个特殊的工厂(目前没有与 Gson 捆绑,应该)称为RuntimeTypeAdapterFactory。复制有关此类的文档说明:

    调整运行时类型可能与其声明不同的值 类型。当字段的类型与该字段的类型不同时,这是必需的 GSON 应该在反序列化该字段时创建。例如, 考虑这些类型:

    { 抽象类形状 { 诠释 x; 整数y; } 类圆形扩展形状{ 内半径; } 类矩形扩展形状{ 整数宽度; 整数高度; } 钻石类扩展形状{ 整数宽度; 整数高度; } 类绘图{ 形状底部形状; 形状顶部形状; } }

    没有额外的类型信息,序列化的 JSON 是不明确的。此图中的底部形状是 长方形还是菱形?

    { { “底部形状”:{ “宽度”:10, “高度”:5, “x”:0, “是”:0 }, “顶部形状”:{ “半径”:2, “x”:4, “是”:1 } }} 此类通过将类型信息添加到序列化 JSON 并尊重该类型来解决此问题 JSON反序列化时的信息:
     {
        {
          “底部形状”:{
            “类型”:“钻石”,
            “宽度”:10,
            “高度”:5,
            “x”:0,
            “是”:0
          },
          “顶部形状”:{
            “类型”:“圆”,
            “半径”:2,
            “x”:4,
            “是”:1
          }
        }}
    类型字段名称 ({@code "type"}) 和类型标签 ({@code "Rectangle"}) 都是可配置的。

    所以你只需要创建你的 Gson 对象:

    RuntimeTypeAdapter<Shape> shapeAdapter
        = RuntimeTypeAdapter.of(Shape.class, "type")
          .registerSubtype(Rectangle.class, "Rectangle");    
    
    Gson gson = new GsonBuilder()
        .registerTypeAdapter(Shape.class, shapeAdapter)
        .create();
    

    瞧,您可以自动(反)序列化对象。我创建了这个工厂的一个变体,而不是期望一个 type 参数,而是尝试通过运行自定义正则表达式来发现什么样的对象(这样你可以避免创建那个额外的参数,或者当你没有完全控制 API)。

    这里我给你两个链接的来源:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-26
      • 1970-01-01
      • 1970-01-01
      • 2015-10-18
      • 2013-10-12
      相关资源
      最近更新 更多