Firebase通用地将映射解析为列表

6

我正在尝试设置一个通用类来从Firebase检索数据,但我在解析部分卡住了。

override fun onDataChange(snapshot: DataSnapshot) {
    try {
        val data: T? = snapshot.getValue(dataType)
        onDataReadFromDatabase(data, d, snapshot, changeListener)
    } catch(e: Exception) {
        d.resumeWithException(e)
    }
} 

T是我的数据类型,dataTypeClass<T>。这对于平面数据结构来说运行得很好,但是当有一个list作为T的子元素时,它会失败,并显示Expected a List while deserializing, but got a class java.util.HashMap

以下是一个失败的数据结构示例:

{
    "id": "xxx",
    "name": "test",
    "items": {
        "a": {"name": "itemA"},
        "b": {"name": "itemB"},
        "c": {"name": "itemC"},
        "d": {"name": "itemD"}
    }
}

使用这样的模型:

data class ItemList(val id: String, val name: String, val items: MutableList<Item>) {
    ...
}

我知道有一种方法可以通过循环来解析子项,就像这里所说的那样(链接),但这是通过知道项目类别来实现的。
我期望的是一种方法,可以告诉Firebase解析器:每次需要将映射转换为列表时,请使用函数x

我尝试使用GenericTypeIndicator,因为它似乎是一个很好的起点,但没有成功。 - MHogge
4个回答

2

您的Json实际上不包含列表,而是一个子对象,其中包含字段"a"、"b"等,这些字段反过来被反序列化为HashMap。

尝试使用列表作为数据:

{
    "id": "xxx",
    "name": "test",
    "items": [
        {"name": "itemA"},
        {"name": "itemB"},
        {"name": "itemC"},
        {"name": "itemD"}
    ]
}

否则,将您的数据类字段更改为
val items: MutableMap<String, Item>

地图键是“a”,“b”等。


2
你需要编写一个自定义反序列化器,然后循环它并获取哈希映射的值。
自定义反序列化器:
public class UserDetailsDeserializer implements JsonDeserializer<AllUserDetailsKeyModel> {
  /*
    bebebejunskjd:{
      "email": "akhilbv1@gmail.com",
          "mobileNum": "12345678",
          "password": "1234567",
          "username": "akhil"}*/
  @Override public AllUserDetailsKeyModel deserialize(JsonElement json, Type typeOfT,
      JsonDeserializationContext context) throws JsonParseException {

    final JsonObject jsonObject = json.getAsJsonObject();

    Gson gson = new Gson();

    Type AllUserDetailsResponseModel =
        new TypeToken<HashMap<String, AllUserDetailsResponseModel>>(){}.getType();

    HashMap<String, AllUserDetailsResponseModel> user =
        gson.fromJson(jsonObject, AllUserDetailsResponseModel);
    AllUserDetailsKeyModel result = new AllUserDetailsKeyModel();
    result.setResult(user);
    return result;
  }


}

以下是需要替换的代码,其中replaceAllUserDetailsKeyModel应该替换为您的模型类,并添加到rest客户端中:

private Converter.Factory createGsonConverter() {
    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(AllUserDetailsKeyModel.class, new UserDetailsDeserializer());
    Gson gson = gsonBuilder.create();
    return GsonConverterFactory.create(gson);
  }

这是Retrofit的自定义转换器。

在你的onResponse方法中,只需循环使用哈希映射并通过键获取值,我的模型类如下所示:

public class AllUserDetailsKeyModel {

  private Map<String, AllUserDetailsResponseModel> result;

  public Map<String, AllUserDetailsResponseModel> getResult() {
    return result;
  }

  public void setResult(Map<String, AllUserDetailsResponseModel> result) {
    this.result = result;
  }

}

可能你需要提供一个类型T,其中T是你的数据类型,我的模型只包含一个哈希映射和相应的getter和setter。

最后像下面这样将自定义转换器设置为retrofit: .addConverterFactory(createGsonConverter())

如果需要更多解释,请告诉我。


1

items不是一个JSON数组,而是一个JSON对象。对于JSON对象,使用映射。因为在映射中,有“键-值”对,因此每对之间都由键区分。

因此,请使用:

data class ItemList(val id: String, val name: String, val items: MutableMap<String, Item>) {
    ...
}

这肯定会解决这个问题。如果您需要更多的解释,请告诉我。


1

您需要更改两个地方。第一个是将您的 ItemList 模型更改为以下内容。

class ItemList constructor() {

var id: Int = 0
var name: String = ""
var items: List<ItemList> = ArrayList()

      constructor(id: Int, name: String, items: List<Item>) : this() {
          this.id = id
          this.name = name
          this.items = items
      }
}

这是因为Firebase需要默认的空构造函数来映射您的值。
现在,您的Firebase数据结构看起来像这样。
{
  id : "1",
  name : "temp",
  items 
      [0] 
         name : "item1"
      [1]
         name : "item2"
}

现在只需在 onDataChanged 方法中映射您的值即可。


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