简体   繁体   中英

Convert JSON String to generic object in JAVA (with GSON)

I have an Api that returns JSON. The response is in some format that can fit into an object called ApiResult and contains a Context <T> and an int Code.

ApiResult is declared in a generic way, eg ApiResult<SomeObject>

I would like to know how to get GSON to convert the incoming JSON String to ApiResult<T>

So far I have:

Type apiResultType = new TypeToken<ApiResult<T>>() { }.getType();
ApiResult<T> result = gson.fromJson(json, apiResultType);

But this still returns converts the Context to a LinkedHashMap instead (which I assume its what GSON falls back to)

You have to know what T is going to be. The incoming JSON is fundamentally just text. GSON has no idea what object you want it to become. If there's something in that JSON that you can clue off of to create your T instance, you can do something like this:

public static class MyJsonAdapter<X> implements JsonDeserializer<ApiResult<X>>
{
    public ApiResult<X> deserialize( JsonElement jsonElement, Type type, JsonDeserializationContext context )
      throws JsonParseException
    {
      String className = jsonElement.getAsJsonObject().get( "_class" ).getAsString();
      try
      {
        X myThing = context.deserialize( jsonElement, Class.forName( className ) );
        return new ApiResult<>(myThing);
      }
      catch ( ClassNotFoundException e )
      {
        throw new RuntimeException( e );
      }
    }
}

I'm using a field "_class" to decide what my X needs to be and instantiating it via reflection (similar to PomPom's example). You probably don't have such an obvious field, but there has to be some way for you to look at the JsonElement and decide based on what's itn it what type of X it should be.

This code is a hacked version of something similar I did with GSON a while back, see line 184+ at: https://github.com/chriskessel/MyHex/blob/master/src/kessel/hex/domain/GameItem.java

My solution is using org.json and Jackson

Below are the methods to wrap a json object into an array, to convert an object to into a list and to convert json string to a type.

   private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

public <T> List<T> parseJsonObjectsToList(JSONObject parentJson, String key, Class<T> clazz) throws IOException {

    Object childObject = parentJson.get(key);

    if(childObject == null) {
        return null;
    }

    if(childObject instanceof JSONArray) {

        JSONArray jsonArray = parentJson.getJSONArray(key);
        return getList(jsonArray.toString(), clazz);

    }

    JSONObject jsonObject = parentJson.getJSONObject(key);
    List<T> jsonList = new ArrayList<>();
    jsonList.add(getObject(jsonObject.toString(), clazz));

    return jsonList;
}

public <T> List<T> getList(String jsonStr, Class clazz) throws IOException {
    ObjectMapper objectMapper = OBJECT_MAPPER;
    TypeFactory typeFactory = objectMapper.getTypeFactory();
    return objectMapper.readValue(jsonStr, typeFactory.constructCollectionType(List.class, clazz));
}

public <T> T getObject(String jsonStr, Class<T> clazz) throws IOException {
    ObjectMapper objectMapper = OBJECT_MAPPER;
    return objectMapper.readValue(jsonStr, clazz);
}

// To call 
  parseJsonObjectsToList(creditReport, JSON_KEY, <YOU_CLASS>.class);

You have to provide Gson the type of T . As gson doesn't know what adapter should be applied, it simply return a data structure.

Your have to provide the generic, like :

Type apiResultType = new TypeToken<ApiResult<String>>() { }.getType();

If type of T is only known at runtime, I use something tricky :

  static TypeToken<?> getGenToken(final Class<?> raw, final Class<?> gen) throws Exception {
    Constructor<ParameterizedTypeImpl> constr = ParameterizedTypeImpl.class.getDeclaredConstructor(Class.class, Type[].class, Type.class);
    constr.setAccessible(true);
    ParameterizedTypeImpl paramType = constr.newInstance(raw, new Type[] { gen }, null);

    return TypeToken.get(paramType);
  }

Your call would be (but replacing String.class with a variable) :

Type apiResultType = getGenToken(ApiResult.class, String.class);

I use JacksonJson library, quite similar to GSon. It's possible to convert json string to some generic type object this way:

String data = getJsonString();
ObjectMapper mapper = new ObjectMapper();
List<AndroidPackage> packages = mapper.readValue(data, List.class);

Maybe this is correct way with GSON in your case:

ApiResult<T> result = gson.fromJson(json, ApiResult.class);

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