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

时间:2013-12-02 11:53:39

标签: java json deserialization 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?

2 个答案:

答案 0 :(得分:3)

要反序列化您的JSON,您需要为Recursive接口提供自定义反序列化器。在这种类中,您需要检查您的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 []}]}]

答案 1 :(得分:1)

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

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

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

  

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

   {
    abstract class Shape {
      int x;
      int y;
    }
    class Circle extends Shape {
      int radius;
    }
    class Rectangle extends Shape {
      int width;
      int height;
    }
    class Diamond extends Shape {
      int width;
      int height;
    }
    class Drawing {
      Shape bottomShape;
      Shape topShape;
    }   }

如果没有其他类型信息,序列化的JSON是不明确的。此图中的底部形状是a   长方形还是钻石?

   {
    {
      "bottomShape": {
        "width": 10,
        "height": 5,
        "x": 0,
        "y": 0
      },
      "topShape": {
        "radius": 2,
        "x": 4,
        "y": 1
      }
    }}
此类通过向序列化JSON添加类型信息并遵守该类型来解决此问题   反序列化JSON时的信息:
   {
    {
      "bottomShape": {
        "type": "Diamond",
        "width": 10,
        "height": 5,
        "x": 0,
        "y": 0
      },
      "topShape": {
        "type": "Circle",
        "radius": 2,
        "x": 4,
        "y": 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。)

在这里,我给出了两个来源链接: