14

我有一个返回 JSON 的 Api。响应采用某种格式,可以放入名为 ApiResult 的对象中,并包含一个Context <T>和一个 int 代码。

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

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

到目前为止,我有:

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

但这仍然返回将 Context 转换为 LinkedHashMap (我假设它是 GSON 回退到的)

4

5 回答 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 所做的类似操作的黑客版本,请参见第 184 行以上:https ://github.com/chriskessel/MyHex/blob/master/src/kessel/hex/domain/GameItem.java

于 2013-07-25T15:06:46.823 回答
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);
于 2013-07-18T20:40:43.243 回答
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);
于 2018-12-13T06:10:35.233 回答
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);
于 2013-07-31T09:59:11.140 回答
0

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));
}
于 2021-09-05T17:41:36.600 回答