簡體   English   中英

改造、Gson 和一系列異構對象

[英]Retrofit, Gson and an array of heterogeneous objects

我正在使用Retrofit向我們的服務器執行 REST 請求。 其中一個請求返回一個對象array ,這些對象一旦在POJO反序列化,就會從abstract class Event擴展而來。 Event 有方法getEventType()返回一個String ,這個字符串是鍵"EventType"的值,我將始終在數組中的JSONObject s中。

這就是JSON樣子(我們現在有 7 種類型的對象):

[
 {
  "EventType":"typeA",
  "Data":"data"
 }, 
 {
  "EventType":"typeB",
  "OtherData":3
 }
] 

我正在嘗試使用RetrofitGSON API在異步調用中反序列化此JSON ,以使用Callback<List<Event>>作為調用的參數,但我仍然找不到方法。

您可以為這種情況編寫自定義 Gson TypeAdapterFactory 事情是確定事件的類型,然后為該類型使用默認的TypeAdapter 這正是我所做的:

public class EventTypeAdapterFactory implements TypeAdapterFactory {
    private static final String TAG = EventTypeAdapterFactory.class.getSimpleName();

    private Map<EventType, TypeAdapter<? extends Event>> ADAPTERS = new ArrayMap<>();
    private TypeAdapter<Event> baseTypeAdapter;
    private TypeAdapter<JsonElement> elementAdapter;
    private TypeAdapter<EventType> eventTypeAdapter;

    @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
      if (!Event.class.isAssignableFrom(type.getRawType())) return null;

      ADAPTERS.put(EventType.TYPE_A, gson.getDelegateAdapter(this, TypeToken.get(TypeAEvent.class)));
      ADAPTERS.put(EventType.TYPE_B, gson.getDelegateAdapter(this, TypeToken.get(TypeBEvent.class)));

      baseTypeAdapter = gson.getDelegateAdapter(this, TypeToken.get(Event.class));

      elementAdapter = gson.getAdapter(JsonElement.class);
      eventTypeAdapter = gson.getAdapter(EventType.class);

      return (TypeAdapter<T>) new EventTypeAdapter().nullSafe();
    }

    private class EventTypeAdapter extends TypeAdapter<Event> {

      @Override public void write(JsonWriter out, Event value) throws IOException {
        EventType eventType = value.getType();
        TypeAdapter<? extends Event> adapter = eventType == null ? baseTypeAdapter : ADAPTERS.get(eventType);
        if (value instanceof TypeAEvent) {
          writeWrap(adapter, out, (TypeAEvent) value, TypeAEvent.class);
        } else if (value instanceof TypeBEvent) {
          writeWrap(adapter, out, (TypeBEvent) value, TypeBEvent.class);
        } else {
          writeWrap(adapter, out, value, Event.class);
        }
      }

      private <T extends Event> void writeWrap(TypeAdapter<? extends Event> adapter,
          JsonWriter out, T value, Class<T> dummyForT) throws IOException {
        ((TypeAdapter<T>)adapter).write(out, value);
      }

      @Override public Event read(JsonReader in) throws IOException {
        JsonObject objectJson = elementAdapter.read(in).getAsJsonObject();
        JsonElement typeJson = objectJson.get("EventType");

        EventType eventType = eventTypeAdapter.fromJsonTree(typeJson);

        if (eventType == null) {
          Log.w(TAG, "Unsupported EventType: " + typeJson);
        }

        TypeAdapter<? extends Event> adapter = eventType == null ? baseTypeAdapter : ADAPTERS.get(eventType);
        return adapter.fromJsonTree(objectJson);
      }
    }
  }

// EventType enum, change to reflect your values.
enum EventType {
    TYPE_A, TYPE_B; 
}

// Base Event type and its successors.
class Event {
    @SerializedName("EventType")
    private EventType type;

    public EventType getType() {
        return type;
    }
}

class TypeAEvent extends Event {
    @SerializedName("Data")
    public String data;
}

class TypeBEvent extends Event {
    @SerializedName("OtherData")
    public int otherData;
}

我不確定,因為我沒有測試過這個,但是如果你寫一個這樣的自定義反序列化器:

private class MyEventDeserialiser implements JsonDeserializer<Event> {

    @Override
    public Event deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {

        JsonArray jArray = (JsonArray) json;

        for (int i=1; i<jArray.size(); i++) {
            JsonObject obj = jArray.get(i).getAsJsonObject();
            String eventType = String.valueOf(obj.get("EventType"));

            //check here which type it is
            Event event = null;

            if(eventType.equals("TypeA")) {
                Event event = context.deserialize(obj, TypeA.class);    
            }
            ...

            return event;
        }
    }
}

然后在用於 Retrofit 的 Gson 反序列化器上設置這個,它可能會起作用。

您可能必須將事件列表封裝在另一個類中,例如

public class EventResponse {
    List<Event> events;
}

然后在您的界面中使用它作為參數,但我不確定。

與 colriot 的回答非常相似。 我做了一些修改,以便將一個類嵌入到 json 中,並且只有在它被序列化時 - 該映射有點難看。

它也不那么健壯,因為我很高興在空值上失敗。

public final class ModelTypeAdapter extends TypeAdapter<Model> {

    private static final String MODEL_CLASS_PROPERTY_NAME = "modelClass";

    private final Gson gson;
    private final TypeAdapterFactory containerFactory;

    @SuppressWarnings("rawtypes")
    private final TypeAdapter<Class> classTypeAdapter;
    private final TypeAdapter<JsonElement> jsonElementAdapter;

    public ModelTypeAdapter(final Gson gson, final TypeAdapterFactory containerFactory) {
        this.gson = gson;
        this.containerFactory = containerFactory;

        this.classTypeAdapter = gson.getAdapter(Class.class);
        this.jsonElementAdapter = gson.getAdapter(JsonElement.class);
    }

    @Override
    public final void write(JsonWriter out, Model value) throws IOException {
        doWrite(out, value);
    }

    private final <M extends Model> void doWrite(JsonWriter out, M value) throws IOException {
        @SuppressWarnings("unchecked")
        final Class<M> modelClass = (Class<M>) value.getClass();

        final TypeAdapter<M> delegateAdapter = gson.getDelegateAdapter(containerFactory, TypeToken.get(modelClass));
        final JsonObject jsonObject = delegateAdapter.toJsonTree(value).getAsJsonObject();
        jsonObject.add(MODEL_CLASS_PROPERTY_NAME, classTypeAdapter.toJsonTree(modelClass));

        jsonElementAdapter.write(out, jsonObject);
    }

    @Override
    public final Model read(JsonReader in) throws IOException {
        final JsonObject jsonObject = jsonElementAdapter.read(in).getAsJsonObject();
        @SuppressWarnings("unchecked")
        final Class<? extends Model> modelClass = classTypeAdapter.fromJsonTree(jsonObject.get(MODEL_CLASS_PROPERTY_NAME));

        jsonObject.remove(MODEL_CLASS_PROPERTY_NAME);
        final TypeAdapter<? extends Model> delegateAdapter = gson.getDelegateAdapter(containerFactory, TypeToken.get(modelClass));

        return delegateAdapter.fromJsonTree(jsonObject);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM