【问题标题】:Convert JSON String to generic object in JAVA (with GSON)将 JSON 字符串转换为 JAVA 中的通用对象(使用 GSON)
【发布时间】:2013-07-17 23:33:07
【问题描述】:

我有一个返回 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 回退到的)

【问题讨论】:

    标签: java gson


    【解决方案1】:

    你必须知道 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

    【讨论】:

    • 我会检查代码,我怀疑我必须按照您的建议做一些事情,但 .NET 中的相同(或相似)代码可以工作。所以这意味着我必须更改通用 API 以适应特定语言? .NET 可以在运行时找到 T 是什么,而我不必在 json 中有一个特殊的 _class 字段。
    • 在不知道 .NET 代码的情况下,我不知道它是如何工作的。 T 没有类值,因此 .NET 不知道要使用哪个类,除非有什么告诉它。
    【解决方案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。最后一条语句(赋值)将如何变化?
    • 是的,我假设你有一个 Class&lt;T&gt; var 来代替 String.class(在 getGenToken 中)。但也许这不适用于您的情况。
    【解决方案3】:

    我的解决方案是使用 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);
    

    【讨论】:

      【解决方案4】:

      我使用 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);
      

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          猜你喜欢
          • 2013-05-19
          • 1970-01-01
          • 2017-01-24
          • 1970-01-01
          • 2017-08-11
          • 2018-01-20
          • 1970-01-01
          • 2021-10-04
          • 2023-03-08
          相关资源
          最近更新 更多