【问题标题】:Gson TypeToken with dynamic ArrayList item type具有动态 ArrayList 项类型的 Gson TypeToken
【发布时间】:2014-01-13 11:04:03
【问题描述】:

我有这个代码:

Type typeOfObjectsList = new TypeToken<ArrayList<myClass>>() {}.getType();
List<myClass> objectsList = new Gson().fromJson(json, typeOfObjectsList);

它将 JSON 字符串转换为 List 的对象。 但是现在我想要这个ArrayList 具有动态类型(不仅仅是myClass),在运行时定义。

ArrayList 的项目类型将使用 reflection 定义。

我试过了:

    private <T> Type setModelAndGetCorrespondingList2(Class<T> type) {
        Type typeOfObjectsListNew = new TypeToken<ArrayList<T>>() {}.getType();
        return typeOfObjectsListNew;
    }

但它不起作用。这是一个例外:

java.sql.SQLException: Fail to convert to internal representation: {....my json....}

【问题讨论】:

  • 这是一个 SQLException。它与您发布的代码无关。向我们展示 JDBC 代码。
  • @SotiriosDelimanolis 不是!我只想让TypeToken&lt;ArrayList&lt;T&gt;&gt;() 代码接受动态 Arraylist 类型。像这样的东西:TypeToken&lt;ArrayList&lt;Class.forName(MyClass)&gt;&gt;

标签: java reflection arraylist gson


【解决方案1】:

从 Gson 2.8.0 开始,您可以使用TypeToken#getParameterized(Type rawType, Type... typeArguments) 创建TypeToken,然后getType() 应该可以解决问题。

例如:

TypeToken.getParameterized(ArrayList.class, myClass).getType()

【讨论】:

    【解决方案2】:

    您提出的语法无效。以下

    new TypeToken<ArrayList<Class.forName(MyClass)>>
    

    无效,因为您试图在需要类型名称的地方传递方法调用。

    以下

    new TypeToken<ArrayList<T>>() 
    

    由于泛型(类型擦除)和反射的工作原理,这是不可能的。整个TypeToken hack 有效,因为Class#getGenericSuperclass() 执行以下操作

    返回表示实体的直接超类的类型 (类、接口、原始类型或 void)由该类表示。

    如果超类是参数化类型,则返回 Type 对象 必须准确反映源中使用的实际类型参数 代码。

    换句话说,如果它看到ArrayList&lt;T&gt;,那就是ParameterizedType,它将返回,您将无法提取类型变量T 本来具有的编译时间值。

    TypeParameterizedType 都是接口。您可以提供自己实现的实例(定义实现任一接口并覆盖其方法的类)或使用helpful factory methods that TypeToken provides in its latest versions 之一。例如,

    private Type setModelAndGetCorrespondingList2(Class<?> typeArgument) {
        return TypeToken.getParameterized(ArrayList.class, typeArgument).getType();
    }
    

    【讨论】:

    • 我认为这是可能的,使用这里显示的技术:stackoverflow.com/questions/14139437/…
    • @BenoitDuffez 小心。您链接的答案是做一些不同的事情。它使用了一个实际的 Class 对象,因此 Gson 可以非常清楚地知道要反序列化的类型。在 OP 的问题中,他们希望在不使用 Class 对象的情况下仅通过 type 参数来执行此操作,该参数在方法本身中不可用。
    • @SotiriosDelimanolis:你说得对。我认为上课是一个可以接受的价格来让这个工作。但实际上,链接的答案与 OP 所要求的有所不同。感谢您的澄清。
    【解决方案3】:

    选项 1 - 自己实现 java.lang.reflect.ParameterizedType 并将其传递给 Gson。

    private static class ListParameterizedType implements ParameterizedType {
    
        private Type type;
    
        private ListParameterizedType(Type type) {
            this.type = type;
        }
    
        @Override
        public Type[] getActualTypeArguments() {
            return new Type[] {type};
        }
    
        @Override
        public Type getRawType() {
            return ArrayList.class;
        }
    
        @Override
        public Type getOwnerType() {
            return null;
        }
    
        // implement equals method too! (as per javadoc)
    }
    

    那么简单:

    Type type = new ListParameterizedType(clazz);
    List<T> list = gson.fromJson(json, type);
    

    注意,按照javadoc,也应该实现equals方法。

    选项 2 -(不要这样做)重用 gson 内部...

    这也可以,至少在 Gson 2.2.4 中也可以。

    Type type = com.google.gson.internal.$Gson$Types.newParameterizedTypeWithOwner(null, ArrayList.class, clazz);
    

    【讨论】:

      【解决方案4】:

      这对我有用:

      public <T> List<T> listEntity(Class<T> clazz)
              throws WsIntegracaoException {
          try {
              // Consuming remote method
              String strJson = getService().listEntity(clazz.getName());
      
              JsonParser parser = new JsonParser();
              JsonArray array = parser.parse(strJson).getAsJsonArray();
      
              List<T> lst =  new ArrayList<T>();
              for(final JsonElement json: array){
                  T entity = GSON.fromJson(json, clazz);
                  lst.add(entity);
              }
      
              return lst;
      
          } catch (Exception e) {
              throw new WsIntegracaoException(
                      "WS method error [listEntity()]", e);
          }
      }
      

      【讨论】:

      【解决方案5】:

      您可以使用 Guava 更强大的 TypeToken 来做到这一点:

      private static <T> Type setModelAndGetCorrespondingList2(Class<T> type) {
          return new TypeToken<ArrayList<T>>() {}
                  .where(new TypeParameter<T>() {}, type)
                  .getType();
      }
      

      【讨论】:

        【解决方案6】:

        使用 kotlin,您可以使用以下函数在一行中转换(从/到)任何(JsonArray/JsonObject),而无需发送 TypeToken:-

        将任何类或数组转换为 JSON 字符串

        inline fun <reified T : Any> T?.json() = this?.let { Gson().toJson(this, T::class.java) }
        

        使用示例:

         val list = listOf("1","2","3")
         val jsonArrayAsString = list.json() 
         //output : ["1","2","3"]
        
         val model= Foo(name = "example",email = "t@t.com") 
         val jsonObjectAsString = model.json()
        //output : {"name" : "example", "email" : "t@t.com"}
        

        将 JSON 字符串转换为任何类或数组

        inline fun <reified T : Any> String?.fromJson(): T? = this?.let {
            val type = object : TypeToken<T>() {}.type
            Gson().fromJson(this, type)
        }
        

        使用示例:

         val jsonArrayAsString = "[\"1\",\"2\",\"3\"]"
         val list = jsonArrayAsString.fromJson<List<String>>()
        
         val jsonObjectAsString = "{ "name" : "example", "email" : "t@t.com"}"
         val model : Foo? = jsonObjectAsString.fromJson() 
         //or 
         val model = jsonObjectAsString.fromJson<Foo>() 
        

        【讨论】:

          【解决方案7】:

          sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl 有效。无需自定义实现

          Type type = ParameterizedTypeImpl.make(List.class, new Type[]{childrenClazz}, null);
          List list = gson.fromJson(json, type);
          

          可用于地图和任何其他集合:

          ParameterizedTypeImpl.make(Map.class, new Type[]{String.class, childrenClazz}, null);
          

          这是一个很好的演示,您可以如何在自定义反序列化器中使用它: Deserializing ImmutableList using Gson

          【讨论】:

            【解决方案8】:

            你真的可以做到。您只需先将数据解析为JsonArray,然后单独转换每个对象,然后将其添加到List

            Class<T> dataType;
            
            //...
            
            JsonElement root = jsonParser.parse(json);
            List<T> data = new ArrayList<>();
            JsonArray rootArray = root.getAsJsonArray();
            for (JsonElement json : rootArray) {
                try {
                    data.add(gson.fromJson(json, dataType));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return data;
            

            【讨论】:

            • try / catch inside loop 效率很低。
            • 在循环内部或外部尝试/捕获对性能没有影响。
            【解决方案9】:

            在 kotlin 中你可以

            inline fun <reified T> parseData(row :String): T{
               return Gson().fromJson(row, object: TypeToken<T>(){}.type)
            }
            

            【讨论】:

              【解决方案10】:

              完全有效的解决方案:

              String json = .... //example: mPrefs.getString("list", "");
              ArrayList<YouClassName> myTypes = fromJSonList(json, YouClassName.class);
              
              
              public <YouClassName> ArrayList<YouClassName> fromJSonList(String json, Class<YouClassName> type) {
                      Gson gson = new Gson();
                      return gson.fromJson(json, TypeToken.getParameterized(ArrayList.class, type).getType());
                  }
              

              【讨论】:

                【解决方案11】:

                其实我已经制作了扩展函数来解决这个问题,将列表保存到 SharedPrefrence 并在应用程序的任何地方检索它们,如下所示:

                使用它来将列表保存到 SharedPref。例如:

                fun <T> SaveList(key: String?, list: List<T>?) {
                val gson = Gson()
                val json: String = gson.toJson(list)
                getSharedPrefrences(App.getAppContext() as Application).edit().putString(key, json).apply()}
                

                从 sharedPref 的任何位置返回列表。像这样:

                fun Context.getList(key: String): String? {
                 return getSharedPrefrences(App.getAppContext() as Application).getString(key, null)}
                
                inline fun <reified T : Any> String?.fromJson(): T? = this?.let {
                val type = object : TypeToken<T>() {}.type
                Gson().fromJson(this, type)}
                

                从已保存的列表中保存和获取列表的用法如下:

                saveList(Consts.SLIDERS, it.data)  
                
                SetSliderData(this.getList(Consts.SLIDERS).fromJson<MutableList<SliderResponseItem>>()!!)
                

                【讨论】:

                  【解决方案12】:

                  这在 Kotlin 中简单明了。

                  val typeDataModelClass = Array<DataModelClass>::class.java
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多