当我将 JSON 从 Firebase 转换为 Java 对象时,为什么会出现“无法反弹到类型”?

2023-12-25

[披露:我是 Firebase 的一名工程师。此问题旨在作为参考问题,一次性回答许多问题。]

我的 Firebase 数据库中有以下 JSON 结构:

{  
  "users": {
    "-Jx5vuRqItEF-7kAgVWy": {
        "handle": "puf",
        "name": "Frank van Puffelen",
        "soId": 209103
    },
    "-Jx5w3IOHD2kRFFgkMbh": {
        "handle": "kato",
        "name": "Kato Wulf",
        "soId": 394010
    },
    "-Jx5x1VWs08Zc5S-0U4p": {
        "handle": "mimming",
        "name": "Jenny Tong",
        "soId": 839465
    }
  }
}

我正在使用以下代码阅读此内容:

private static class User {
    String handle;
    String name;

    public String getHandle() { return handle; }
    public String getName() { return name; }
}

Firebase ref = new Firebase("https://stackoverflow.firebaseio.com/32108969/users");

ref.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot usersSnapshot) {
        for (DataSnapshot userSnapshot : usersSnapshot.getChildren()) {
          User user = userSnapshot.getValue(User.class);
          System.out.println(user.toString());
        }
    }

    @Override
    public void onCancelled(FirebaseError firebaseError) { }
});

但我收到这个错误:

线程“FirebaseEventTarget”com.firebase.client.FirebaseException 中出现异常:无法弹回键入

如何将我的用户读入 Java 对象?


Firebase 使用 Jackson 允许将 Java 对象序列化为 JSON 以及将 JSON 反序列化回 Java 对象。您可以在以下位置找到有关杰克逊的更多信息杰克逊网站 https://github.com/FasterXML/jackson和这个关于 Jackson 注释的页面 http://www.baeldung.com/jackson-annotations.

在本答案的其余部分中,我们将展示将 Jackson 与 Firebase 结合使用的几种常见方法。

加载完整用户

将用户从 Firebase 加载到 Android 的最简单方法是创建一个完全模仿 JSON 中属性的 Java 类:

private static class User {
  String handle;
  String name;
  long stackId;

  public String getHandle() { return handle; }
  public String getName() { return name; }
  public long getStackId() { return stackId; }

  @Override
  public String toString() { return "User{handle='"+handle+“', name='"+name+"', stackId="+stackId+"\’}”; }
}

我们可以在侦听器中使用此类:

Firebase ref = new Firebase("https://stackoverflow.firebaseio.com/32108969/users");

ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot usersSnapshot) {
    for (DataSnapshot userSnapshot : usersSnapshot.getChildren()) {
      User user = userSnapshot.getValue(User.class);
      System.out.println(user.toString());
    }
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) { }
});

您可能会注意到 User 类遵循JavaBean属性模式 https://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html。每个 JSON 属性都由 User 类中的一个字段映射,并且每个字段都有一个公共 getter 方法。通过确保所有属性都使用完全相同的名称进行映射,我们确保 Jackson 可以自动映射它们。

您还可以通过在 Java 类及其字段和方法上添加 Jackson 注释来手动控制映射。我们将介绍两个最常见的注释(@JsonIgnore and @JsonIgnoreProperties) below.

部分加载用户

假设您只关心 Java 代码中的用户名和句柄。让我们删除stackId看看会发生什么:

private static class User {
  String handle;
  String name;

  public String getHandle() { return handle; }
  public String getName() { return name; }

  @Override
  public String toString() { 
    return "User{handle='" + handle + “\', name='" + name + "\’}”; 
  }
}

如果我们现在附加与之前相同的侦听器并运行程序,它将抛出异常:

Exception in thread "FirebaseEventTarget" com.firebase.client.FirebaseException: Failed to bounce to type

at com.firebase.client.DataSnapshot.getValue(DataSnapshot.java:187)

at com.firebase.LoadPartialUsers$1.onDataChange(LoadPartialUsers.java:16)

“无法反跳类型”表示 Jackson 无法将 JSON 反序列化为 User 对象。在嵌套异常中,它告诉我们原因:

Caused by: com.shaded.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "stackId" (class com.firebase.LoadPartialUsers$User), not marked as ignorable (2 known properties: , "handle", "name"])

 at [Source: java.io.StringReader@43079089; line: 1, column: 15] (through reference chain: com.firebase.User["stackId"])

at com.shaded.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:79)

杰克逊找到了一处房产stackId在 JSON 中,并且不知道如何处理它,因此它抛出异常。幸运的是,我们可以使用一个注释来告诉它在将 JSON 映射到我们的数据时忽略 JSON 中的特定属性。User class:

@JsonIgnoreProperties({ "stackId" })
private static class User {
  ...
}

如果我们不再使用侦听器运行代码,杰克逊就会知道它可以忽略stackId在 JSON 中,它将能够再次将 JSON 反序列化为 User 对象。

由于向 JSON 添加属性是 Firebase 应用程序中的常见做法,因此您可能会发现简单地告诉 Jackson 忽略 Java 类中没有映射的所有属性会更方便:

@JsonIgnoreProperties(ignoreUnknown=true)
private static class User {
  ...
}

现在,如果我们稍后向 JSON 添加属性,Java 代码仍然能够加载Users。请记住,User 对象不会包含 JSON 中存在的所有信息,因此再次将它们写回 Firebase 时要小心。

部分保存用户

拥有一个自定义 Java 类是件好事,原因之一是我们可以向它添加方便的方法。假设我们添加一个方便的方法来获取要为用户显示的名称:

private static class User {
  String handle;
  String name;

  public String getHandle() { return handle; }
  public String getName() { return name; }

  @JsonIgnore
  public String getDisplayName() {
    return getName() + " (" + getHandle() + ")";
  }

  @Override
  public String toString() { 
    return "User{handle='" + handle + "\', name='" + name + "\', displayName='" + getDisplayName() + "'}"; 
  }
}

现在让我们从 Firebase 读取用户并将其写回新位置:

Firebase srcRef = new Firebase("https://stackoverflow.firebaseio.com/32108969/users");
final Firebase copyRef = new Firebase("https://stackoverflow.firebaseio.com/32108969/copiedusers");

srcRef.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot usersSnapshot) {
    for (DataSnapshot userSnapshot : usersSnapshot.getChildren()) {
      User user = userSnapshot.getValue(User.class);
      copyRef.child(userSnapshot.getKey()).setValue(user);
    }
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) { }
});

中的 JSONcopiedusers节点看起来像这样:

"copiedusers": {
    "-Jx5vuRqItEF-7kAgVWy": {
        "displayName": "Frank van Puffelen (puf)",
        "handle": "puf",
        "name": "Frank van Puffelen"
    },
    "-Jx5w3IOHD2kRFFgkMbh": {
        "displayName": "Kato Wulf (kato)",
        "handle": "kato",
        "name": "Kato Wulf"
    },
    "-Jx5x1VWs08Zc5S-0U4p": {
        "displayName": "Jenny Tong (mimming)",
        "handle": "mimming",
        "name": "Jenny Tong"
    }
}

这与源 JSON 不同,因为 Jackson 识别新的getDisplayName()方法作为 JavaBean getter,因此添加了一个displayName属性到它输出的 JSON。我们通过添加一个来解决这个问题JsonIgnore注释到getDisplayName().

    @JsonIgnore
    public String getDisplayName() {
        return getName() + "(" + getHandle() + ")";
    }

当序列化 User 对象时,Jackson 现在将忽略getDisplayName()方法和我们写出的 JSON 将与我们得到的相同。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当我将 JSON 从 Firebase 转换为 Java 对象时,为什么会出现“无法反弹到类型”? 的相关文章

随机推荐