使用GSON在JAVA中将JSON字符串转换为通用对象

14

我有一个返回JSON的API。响应以某种格式返回,可以适应名为ApiResult的对象,并包含Context<T>和int Code。

ApiResult以通用方式声明,例如ApiResult<SomeObject>。

我想知道如何让GSON将传入的JSON字符串转换为ApiResult<T>。

目前我的做法是:

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

但是这仍然会将上下文转换为LinkedHashMap(我认为这是GSON退回的方式)。

5个回答

5

您需要知道 T 将成为什么。传入的 JSON 基本上只是文本。GSON 不知道您想要将其转换为什么对象。如果在 JSON 中有一些可以提示您创建 T 实例的线索,您可以执行以下操作:

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 );
      }
    }
}

我使用一个名为"_class"的字段来决定我的X应该是什么,并通过反射实例化它(类似于PomPom的示例)。您可能没有这样显然的字段,但必须有某种方法让您查看JsonElement并根据其中的内容决定应该使用哪种类型的X。
这段代码是我之前用GSON做类似事情的一个被hack过的版本,请参考第184行及其后面:https://github.com/chriskessel/MyHex/blob/master/src/kessel/hex/domain/GameItem.java

我将检查代码,我怀疑我需要像你建议的那样做一些事情,但是在.NET中相同(或类似)的代码可以工作。这意味着我必须更改通用API以适应特定语言?.NET可以在运行时找到T而无需我在json中拥有特殊的_class字段。 - Yannis
不了解.NET代码,我不知道那该怎么做。T没有类值,所以.NET不知道要使用哪个类,除非有些东西告诉它。 - Chris Kessel

2

将JSON转换为通用对象

public <T> T fromJson(String json, Class<T> clazz) {
    return new Gson().fromJson(json, clazz);
}

将JSON转换为通用对象列表

public <T> List<T> fromJsonAsList(String json, Class<T[]> clazz) {
    return Arrays.asList(new Gson().fromJson(json, clazz));
}

如何调用 fromJsonAsList 方法? - Nisarg Patil
fromJsonAsList(json, SomeClass[].class) - Nisarg Patil

2

您需要向 Gson 提供 T 的类型。由于 Gson 不知道应该使用哪个适配器,它只会返回一个数据结构。

您需要提供泛型,例如:

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

如果T的类型只在运行时才能确定,我会使用一些巧妙的方法:
  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);
  }

您的调用将是(但会用一个变量替换 String.class):

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

所以我理解在你的例子中T是String类型。但是我没有一个String,而是一个T。最后一条语句(赋值)会如何改变? - Yannis
是的,我假设你有一个Class<T>变量可以替换getGenToken中的String.class。但也许这并不适用于你的情况。 - PomPom

2

我的解决方案是使用org.json和Jackson

以下是将json对象封装为数组、将对象转换为列表以及将json字符串转换为类型的方法。

   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);

0

我使用JacksonJson库,它与GSon非常相似。可以通过以下方式将json字符串转换为某些通用类型的对象:

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

也许这是在您的情况下使用GSON的正确方式:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接