简体   繁体   中英

GSON deserialize two different object type on same list

I'm trying to consume an API that have a return like:

{
   "data":{
      "id":"12345",
      "name":"Some Name",
      "regions":[
         "r987",
         "r654"
      ]
   }
}

that the region attribute is an List<String> with only the regions ID.
But I can ask to the same request retrieve the full region object, so the json change to:

{
   "data":{
      "id":"12345",
      "name":"Some Name",
      "regions":{
         "data":[
            {
               "id":"r987",
               "name":"region 1"
            },
            {
               "id":"r654",
               "name":"region 2"
            }
         ]
      }
   }
}

I thought of creating an adapter to always translate the list of Id to return a list with the full object, like

class RegionListAdapter implements JsonDeserializer<List<Region>> {

    @Override
    public List<Region> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext jsc) throws JsonParseException {

        List<Region> result = new ArrayList<>();

        if (json.isJsonObject()) {
            JsonObject envelope = json.getAsJsonObject();
            JsonArray data = envelope.getAsJsonArray("data");
            result = jsc.deserialize(data, typeOfT);
        } else {
            JsonArray idArray = json.getAsJsonArray();
            for(JsonElement e : idArray){
                String regionId = e.getAsString();
                Region region = new Region(regionId);
                result.add(region);
            }
        }

        return result;
    }
}

and with this everything work, receiving only the ID or the full object...

The thing is, I have others attributes that work on the same way. Is there any way to make this adapter work generically or do it differently? Or I need to create a Adapter for each object?

The hard part of making this generic is that you need to create new instances of the object type that is in the list. If all the objects are going to use the field named id as what is going in the list version, we can use that to create a new instance. The easiest hack is shown below, where we stuff the string as the id field into a new JsonObject and use Gson to construct the new object.

class IdListAdapter<T> implements JsonDeserializer<List<T>> {

  @Override
  public List<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext jsc) throws JsonParseException {

    if (json.isJsonObject()) {
      JsonObject envelope = json.getAsJsonObject();
      JsonArray data = envelope.getAsJsonArray("data");
      return jsc.deserialize(data, typeOfT);
    } else {
      JsonArray idArray = json.getAsJsonArray();
      List<T> result = new ArrayList<T>(idArray.size());
      for (JsonElement id : idArray) {
        if (typeOfT instanceof ParameterizedType) {
          Type parameterType = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
          JsonObject obj = new JsonObject();
          obj.add("id", id);
          T element = jsc.deserialize(obj, parameterType);
          result.add(element);
        }
      }
      return result;
    }
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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