簡體   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