简体   繁体   English

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

[英]Deserialize recursive polymorphic class in GSON

class Complex implements Recursive {
  Map<String, Recursive> map;
  ...
}

class Simple implements Recursive { ... }

How do I deserialize this json: 我如何反序列化此json:

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

using Google GSON? 使用Google GSON?

To deserialize your JSON you need a custom deserializer for your Recursive interface. 要反序列化JSON,您需要为递归接口使用自定义反序列化器。 In that kind of class you need to examine your JSON and decide what kind of class to instantiate as the type field in the JSON itself. 在这种类中,您需要检查JSON并确定要实例化为JSON本身的type字段的类。 Here you have a basic deserializer I wrote for you example. 在这里,您有一个我为您编写的示例基本解串器。

Of course it can be improve to manage borderline cases (for example, what happens if you do not have type field?). 当然,管理边界事件可以得到改善(例如,如果您没有类型字段,会发生什么情况?)。

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

   }

}

This is the result of my code: 这是我的代码的结果:

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

Sorry for replying too late (as this question is already answered). 抱歉回复太晚(因为此问题已得到回答)。 But I think I can give my 2 cents to this issue. 但是我想我可以给这个问题2美分。

As @Parobay has said before you can add an special {"type": "complex"} parameter in order to know which class do you want to deserialize. 正如@Parobay所说的那样,您可以添加一个特殊的{"type": "complex"}参数以了解要反序列化的类。 But as said before, you have to move all your data to an {"instance": {}} subobject which feels akward as you have to keep control manually of the instances created in a big switch . 但是如前所述,您必须将所有数据移动到一个{"instance": {}}子对象,该子对象感觉很笨拙,因为您必须手动控制在big switch创建的实例。

You can use instead a TypeFactory class to handle this situation better. 您可以改用TypeFactory类来更好地处理这种情况。 More precisely, there are one special factory (which is not bundled currently with Gson and should) called RuntimeTypeAdapterFactory . 更确切地说,有一个特殊的工厂(该工厂目前未与Gson捆绑在一起,应该将其命名为RuntimeTypeAdapterFactory Copying what documentation says about this class: 复制有关该类的文档说明:

Adapts values whose runtime type may differ from their declaration type. 适应其运行时类型可能不同于其声明类型的值。 This is necessary when a field's type is not the same type that GSON should create when deserializing that field. 当字段的类型与GSON反序列化该字段时应创建的类型不同时,这是必需的。 For example, consider these types: 例如,考虑以下类型:

 { {\n    abstract class Shape { 抽象类Shape {\n      int x; int x;\n      int y; 诠释\n    } }\n    class Circle extends Shape { 类Circle扩展Shape {\n      int radius; 整数半径\n    } }\n    class Rectangle extends Shape { 矩形扩展Shape的类{\n      int width; 整数宽度\n      int height; 整数高度\n    } }\n    class Diamond extends Shape { 钻石级延伸形状{\n      int width; 整数宽度\n      int height; 整数高度\n    } }\n    class Drawing { 类图{\n      Shape bottomShape; 形状bottomShape;\n      Shape topShape; 形状topShape;\n    } } }} 

Without additional type information, the serialized JSON is ambiguous. 如果没有其他类型信息,则序列化的JSON不明确。 Is the bottom shape in this drawing a rectangle or a diamond? 该图中的底部形状是矩形还是菱形?

 { {\n    { {\n      "bottomShape": { “ bottomShape”:{\n        "width": 10, “宽度”:10,\n        "height": 5, “身高”:5\n        "x": 0, “ x”:0,\n        "y": 0 “ y”:0\n      }, },\n      "topShape": { “ topShape”:{\n        "radius": 2, “半径”:2\n        "x": 4, “ x”:4\n        "y": 1 “ y”:1\n      } }\n    }} }} 
This class addresses this problem by adding type information to the serialized JSON and honoring that type information when the JSON is deserialized: 此类通过将类型信息添加到序列化的JSON并在反序列化JSON时保留该类型信息来解决此问题:
 { {\n    { {\n      "bottomShape": { “ bottomShape”:{\n        "type": "Diamond", “ type”:“ Diamond”,\n        "width": 10, “宽度”:10,\n        "height": 5, “身高”:5\n        "x": 0, “ x”:0,\n        "y": 0 “ y”:0\n      }, },\n      "topShape": { “ topShape”:{\n        "type": "Circle", “ type”:“ Circle”,\n        "radius": 2, “半径”:2\n        "x": 4, “ x”:4\n        "y": 1 “ y”:1\n      } }\n    }} }} 
Both the type field name ({@code "type"}) and the type labels ({@code "Rectangle"}) are configurable. 类型字段名称({@code“ type”})和类型标签({@code“ Rectangle”})都是可配置的。

So you only have to create your Gson object with: 因此,您只需要使用以下命令创建Gson对象:

RuntimeTypeAdapter<Shape> shapeAdapter
    = RuntimeTypeAdapter.of(Shape.class, "type")
      .registerSubtype(Rectangle.class, "Rectangle");    

Gson gson = new GsonBuilder()
    .registerTypeAdapter(Shape.class, shapeAdapter)
    .create();

And voilá, you have automatic (de)serialization of objects. 而且,您可以自动(反)序列化对象。 I created a variation of this Factory that instead of expecting a type parameter, tries to discover what kind of object is by running a custom regular expression (that way you can avoid to create that extra parameter, or when you don't have full control of the API). 我创建了这个Factory的变体,而不是期望type参数,而是尝试通过运行自定义正则表达式来发现什么样的对象(这样可以避免创建额外的参数,或者当您没有完全控制权时API)。

Here I give you the two links with the sources: 在这里,我为您提供了两个带有来源的链接:

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

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