在 GSON 中反序列化递归多态类


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?

要反序列化 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 []}]}]

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

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

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

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

{    抽象类形状 {      整数 x;      国际 y;    }    类 圆 延伸 形状 {      整数半径;    }    类 矩形 扩展形状 {      整数宽度;      整数高度;    }    类 钻石延伸 形状 {      整数宽度;      整数高度;    }    类绘图 {      形状底部形状;      形状顶部形状;    } } 如果没有

其他类型信息,序列化的 JSON 是不明确的。此图中的底部形状是 矩形还是菱形?

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

因此,您只需使用以下命令创建 Gson 对象:

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

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

在这里,我为您提供两个来源的链接:

  • 原始运行时类型适配器工厂源代码。
  • 我的修改运行时正则表达式类型适配器出厂源代码。

最新更新