【问题标题】:Gson handle object or arrayGson 句柄对象或数组
【发布时间】:2011-10-05 22:29:52
【问题描述】:

我有以下课程

public class MyClass {
    private List<MyOtherClass> others;
}

public class MyOtherClass {
    private String name;
}

我有可能看起来像这样的 JSON

{
  others: {
    name: "val"
  }
}

或者这个

{
  others: [
    {
      name: "val"
    },
    {
      name: "val"
    }
  ]
}

我希望能够对这两种 JSON 格式使用相同的 MyClass。有没有办法用 Gson 做到这一点?

【问题讨论】:

  • 问题是,这样的Json是谁生成的?它是有效的Json吗?如果是,Gson 应该处理它。如果没有,“真正的”解决方案应该是修复生产者。
  • 我完全同意这不是编写 JSON 的好方法。不幸的是,我们并不总是能够控制我们使用的数据,因此修复生产者并不总是一种选择。它是有效的 JSON,因为 JSON 是无模式的。
  • 同意@three-cups

标签: gson


【解决方案1】:

我想出了一个答案。

private static class MyOtherClassTypeAdapter implements JsonDeserializer<List<MyOtherClass>> {
    public List<MyOtherClass> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx) {
        List<MyOtherClass> vals = new ArrayList<MyOtherClass>();
        if (json.isJsonArray()) {
            for (JsonElement e : json.getAsJsonArray()) {
                vals.add((MyOtherClass) ctx.deserialize(e, MyOtherClass.class));
            }
        } else if (json.isJsonObject()) {
            vals.add((MyOtherClass) ctx.deserialize(json, MyOtherClass.class));
        } else {
            throw new RuntimeException("Unexpected JSON type: " + json.getClass());
        }
        return vals;
    }
}

像这样实例化一个 Gson 对象

Type myOtherClassListType = new TypeToken<List<MyOtherClass>>() {}.getType();

Gson gson = new GsonBuilder()
        .registerTypeAdapter(myOtherClassListType, new MyOtherClassTypeAdapter())
        .create();

TypeTokencom.google.gson.reflect.TypeToken

您可以在此处阅读解决方案:

https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener

【讨论】:

  • 谢谢,完美运行。我遇到过 JSON 会返回单个对象或一组对象的情况。
  • 谢谢。这很棒。我需要两个不同的 TypeAdapter,所以我通过添加另一个 .registerTypeAdapter 来链接它们
  • 为什么 Gson 不只放注释来接受相同格式的数组或对象?这是非常需要的。
  • 酷!并且只是提醒别人,别忘了implements JsonDeserializer&lt;List&lt;MyOtherClass&gt;&gt;,否则不起作用
【解决方案2】:

感谢三杯的解决方案!

与泛型类型相同,以防多种类型需要它:

public class SingleElementToListDeserializer<T> implements JsonDeserializer<List<T>> {

private final Class<T> clazz;

public SingleElementToListDeserializer(Class<T> clazz) {
    this.clazz = clazz;
}

public List<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
    List<T> resultList = new ArrayList<>();
    if (json.isJsonArray()) {
        for (JsonElement e : json.getAsJsonArray()) {
            resultList.add(context.<T>deserialize(e, clazz));
        }
    } else if (json.isJsonObject()) {
        resultList.add(context.<T>deserialize(json, clazz));
    } else {
        throw new RuntimeException("Unexpected JSON type: " + json.getClass());
    }
    return resultList;
    }
}

并配置 Gson:

Type myOtherClassListType = new TypeToken<List<MyOtherClass>>() {}.getType();
SingleElementToListDeserializer<MyOtherClass> adapter = new SingleElementToListDeserializer<>(MyOtherClass.class);
Gson gson = new GsonBuilder()
    .registerTypeAdapter(myOtherClassListType, adapter)
    .create();

【讨论】:

    【解决方案3】:

    共享代码,并且仅将反序列化逻辑应用于特定字段:

    JSON 模型:

    public class AdminLoginResponse implements LoginResponse
    {
        public Login login;
        public Customer customer;
        @JsonAdapter(MultiOrganizationArrayOrObject.class)    // <-------- look here
        public RealmList<MultiOrganization> allAccounts;
    }
    

    字段特定类:

    class MultiOrganizationArrayOrObject
        : ArrayOrSingleObjectTypeAdapter<RealmList<MultiOrganization>,MultiOrganization>(kClass()) {
        override fun List<MultiOrganization>.toTypedList() = RealmList(*this.toTypedArray())
    }
    

    抽象类:

    /**
     * parsed field can either be a [JSONArray] of type [Element], or an single [Element] [JSONObject].
     */
    abstract class ArrayOrSingleObjectTypeAdapter<TypedList: List<Element>, Element : Any>(
        private val elementKClass: KClass<Element>
    ) : JsonDeserializer<TypedList> {
        override fun deserialize(
            json: JsonElement, typeOfT: Type?, ctx: JsonDeserializationContext
        ): TypedList = when {
            json.isJsonArray -> json.asJsonArray.map { ctx.deserialize<Element>(it, elementKClass.java) }
            json.isJsonObject -> listOf(ctx.deserialize<Element>(json, elementKClass.java))
            else -> throw RuntimeException("Unexpected JSON type: " + json.javaClass)
        }.toTypedList()
        abstract fun List<Element>.toTypedList(): TypedList
    }
    

    【讨论】:

      【解决方案4】:

      基于三杯答案,我有以下内容可以让 JsonArray 直接反序列化为数组。

      static public <T> T[] fromJsonAsArray(Gson gson, JsonElement json, Class<T> tClass, Class<T[]> tArrClass)
              throws JsonParseException {
      
          T[] arr;
      
          if(json.isJsonObject()){
              //noinspection unchecked
              arr = (T[]) Array.newInstance(tClass, 1);
              arr[0] = gson.fromJson(json, tClass);
          }else if(json.isJsonArray()){
              arr = gson.fromJson(json, tArrClass);
          }else{
              throw new RuntimeException("Unexpected JSON type: " + json.getClass());
          }
      
          return arr;
      }
      

      用法:

          String response = ".......";
      
          JsonParser p = new JsonParser();
          JsonElement json = p.parse(response);
          Gson gson = new Gson();
          MyQuote[] quotes = GsonUtils.fromJsonAsArray(gson, json, MyQuote.class, MyQuote[].class);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-20
        • 2015-06-12
        • 1970-01-01
        相关资源
        最近更新 更多