繁体   English   中英

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

[英]Deserialize recursive polymorphic class in 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"
        } 
     } 
  }
}

使用Google GSON?

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

当然,管理边界事件可以得到改善(例如,如果您没有类型字段,会发生什么情况?)。

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": {}}子对象,该子对象感觉很笨拙,因为您必须手动控制在big switch创建的实例。

您可以改用TypeFactory类来更好地处理这种情况。 更确切地说,有一个特殊的工厂(该工厂目前未与Gson捆绑在一起,应该将其命名为RuntimeTypeAdapterFactory 复制有关该类的文档说明:

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

  {\n     抽象类Shape {\n       int x;\n       诠释\n     }\n     类Circle扩展Shape {\n       整数半径\n     }\n     矩形扩展Shape的类{\n       整数宽度\n       整数高度\n     }\n     钻石级延伸形状{\n       整数宽度\n       整数高度\n     }\n     类图{\n       形状bottomShape;\n       形状topShape;\n     }} 

如果没有其他类型信息,则序列化的JSON不明确。 该图中的底部形状是矩形还是菱形?

  {\n     {\n       “ bottomShape”:{\n         “宽度”:10,\n         “身高”:5\n         “ x”:0,\n         “ y”:0\n       },\n       “ topShape”:{\n         “半径”:2\n         “ x”:4\n         “ y”:1\n       }\n     }} 
此类通过将类型信息添加到序列化的JSON并在反序列化JSON时保留该类型信息来解决此问题:
  {\n     {\n       “ bottomShape”:{\n         “ type”:“ Diamond”,\n         “宽度”:10,\n         “身高”:5\n         “ x”:0,\n         “ y”:0\n       },\n       “ topShape”:{\n         “ type”:“ Circle”,\n         “半径”:2\n         “ x”:4\n         “ y”:1\n       }\n     }} 
类型字段名称({@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();

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

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

暂无
暂无

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

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