我的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中的异常:无法反弹以键入
“ FirebaseEventTarget” com.firebase.client.FirebaseException
如何将用户读入Java对象?
Firebase使用Jackson允许将Java对象序列化为JSON,并允许将JSON反序列化为Java对象。你可以在Jackson网站上找到有关Jackson的更多信息,以及此页面上有关Jackson注释的信息。
在此答案的其余部分中,我们将展示在Firebase中使用Jackson的几种常见方法。
加载完整的用户
将用户从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属性模式。每个JSON属性均通过User类中的一个字段进行映射,并且每个字段都有一个公共的getter方法。通过确保所有属性都使用完全相同的名称映射,我们确保Jackson可以自动映射它们。
你还可以通过在Java类及其字段和方法上放置Jackson注释来手动控制映射。我们将在下面介绍两个最常见的注释(@JsonIgnore和@JsonIgnoreProperties)。
@JsonIgnore和@JsonIgnoreProperties
部分加载用户 假设你只在意Java代码中的用户名和句柄。让我们删除stackId,看看会发生什么:
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)
杰克逊(Jackson)stackId在JSON中找到了一个属性,但不知道该如何处理,因此引发了异常。幸运的是,有一个注释,当将其映射到我们的User类时,可以用来告诉它忽略JSON中的特定属性:
User
@JsonIgnoreProperties({ “stackId" }) private static class User { ... }
如果我们不再次使用侦听器运行代码,Jackson将知道它可以stackId在JSON中忽略,并且能够再次将JSON反序列化为User对象。
由于在Firebase应用程序中将属性添加到JSON是一种常见的做法,因此你可能会发现,简单地告诉Jackson忽略Java类中没有映射的所有属性会更方便:
@JsonIgnoreProperties(ignoreUnknown=true) private static class User { ... }
现在,如果稍后再将属性添加到JSON,则Java代码仍将能够加载User。请记住,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) { } });
copiedusers节点中的JSON 如下所示:
copiedusers
"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不同,因为杰克逊将新getDisplayName()方法识别为JavaBean getter,因此displayName向其输出的JSON 添加了一个属性。我们通过向添加JsonIgnore注释来解决此问题getDisplayName()。
getDisplayName()
displayName
JsonIgnore
@JsonIgnore public String getDisplayName() { return getName() + "(" + getHandle() + ")"; }
序列化User对象时,Jackson现在将忽略该getDisplayName()方法,我们写出的JSON与获得的JSON相同。