当我将Firebase中的JSON转换为Java对象时,为什么会出现“无法popupinput”?

[披露:我是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”中的exceptioncom.firebase.client.FirebaseException:无法反弹

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

Firebase使用Jackson将Java对象序列化为JSON,并将JSON反序列化为Java对象。 您可以在jackson网站上find关于jackson的更多信息,以及关于jackson注释的信息 。

在这个答案的其余部分,我们将展示几种使用Firebase的常用方法。

加载完整的用户

将用户从Firebase加载到Android的最简单方法是,如果我们创build一个完全模仿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可以自动映射它们。

您还可以通过将Jackson注释放在您的Java类及其字段和方法上来手动控制映射。 我们将覆盖下面两个最常见的注释( @JsonIgnore@JsonIgnoreProperties )。

部分加载用户

假设你只关心用户的名字并在你的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:

 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) 

“未能反弹types”表示Jackson无法将JSON反序列化为User对象。 在嵌套exception中,它告诉我们为什么:

 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在JSON中发现了一个属性stackId ,不知道该如何处理,所以会引发exception。 幸运的是,我们可以使用一个注释来告诉它在映射到User类时忽略JSON的特定属性:

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

如果我们不再用我们的监听器运行代码,Jackson会知道它可以忽略JSON中的stackId ,并且能够将JSON反序列化为User对象。

由于向JSON添加属性在Firebase应用程序中是很常见的做法,因此您可能会发现,仅仅告诉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": { "-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,因此向其输出的JSON添加了displayName属性。 我们通过将JsonIgnore注释添加到getDisplayName()来解决此问题。

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

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

用于Android / Java的9.x版(或更高版本)的Firebase SDK已停止,包括Jackson用于序列化/反序列化Java < – > JSON。 较新的SDK改为提供最less量的自定义注释,以便控制最常见的自定义需求,同时对生成的JAR / APK大小的影响最小。

我之前给出的答案仍然有效,如果你是:

  • 使用Firebase 2.x SDK
  • 使用Firebase 9.0或更高版本的SDK,但[使用Jackson进行序列化/反序列化Java < – > JSON](TODO:链接来回答)。

数据结构

我们将从Firebase数据库中的这个JSON结构开始:

 { "-Jx86I5e8JBMZ9tH6W3Q" : { "handle" : "puf", "name" : "Frank van Puffelen", "stackId" : 209103, "stackOverflowId" : 209103 }, "-Jx86Ke_fk44EMl8hRnP" : { "handle" : "mimming", "name" : "Jenny Tong", "stackId" : 839465 }, "-Jx86N4qeUNzThqlSMer" : { "handle" : "kato", "name" : "Kato Wulf", "stackId" : 394010 } } 

加载完整的用户

从最基本的angular度来说,我们可以将每个用户从这个JSON加载到下面的Java类中:

 private static class CompleteUser { 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+ "'}"; } } 

如果我们宣布这些领域是公开的,我们甚至不需要获得者:

 private static class CompleteUser { public String handle; public String name; public long stackId; } 

部分加载用户

我们也可以部分加载用户,例如:

 private static class PartialUser { String handle; String name; public String getHandle() { return handle; } public String getName() { return name; } @Override public String toString() { return "User{handle='" + handle + "', NAME='" + name + "''}"; } } 

当我们使用这个类从相同的JSON加载用户时,代码运行(与我的其他答案中提到的Jackson变体不同)。 但是你会在日志输出中看到一个警告:

警告:在类Annotations $ PartialUser上找不到stackId的setter / field

所以摆脱这一点,我们可以用@IgnoreExtraProperties注解类:

 @IgnoreExtraProperties private static class PartialUser { String handle; String name; public String getHandle() { return handle; } public String getName() { return name; } @Override public String toString() { return "User{handle='" + handle + "', NAME='" + name + "''}"; } } 

部分保存用户

和以前一样,您可能需要将计算的属性添加到用户。 将数据保存回数据库时,您会希望忽略这样的属性。 要做到这一点,你可以用@Exclude注释属性/ getter / setter / field:

 private static class OvercompleteUser { String handle; String name; long stackId; public String getHandle() { return handle; } public String getName() { return name; } public long getStackId() { return stackId; } @Exclude public String getTag() { return getName() + " ("+getHandle()+")"; } @Override public String toString() { return "User{handle='"+handle+"', name='"+name+"', stackId="+stackId+ "'}"; } } 

现在将用户写入数据库时​​, getTag()的值将被忽略。

在JSON中使用不同的属性名称,而不是在Java代码中

您还可以指定Java代码中的字段/ getter / setter应该在数据库的JSON中获得的名称。 为此:使用@PropertyName()注释字段/ getter / setter。

 private static class UserWithRenamedProperty { String handle; String name; @PropertyName("stackId") long stackOverflowId; public String getHandle() { return handle; } public String getName() { return name; } @PropertyName("stackId") public long getStackOverflowId() { return stackOverflowId; } @Override public String toString() { return "User{handle='"+handle+"', name='"+name+"', stackId="+stackOverflowId+ "'}"; } } 

通常,最好使用Firebase SDK使用的Java < – > JSON之间的默认映射。 但是,如果您有一个预先存在的JSON结构,而您无法映射到Java类,则可能需要@PropertyName

因为root.this中错误的查询path文件夹就是例子 在这里输入图像描述

 My code: private void GetUpdates(DataSnapshot snapshot){ romchat.clear(); for (DataSnapshot ds: snapshot.getChildren()){ Rowitemroom row = new Rowitemroom(); row.setCaption(ds.getValue(Rowitemroom.class).getCaption()); row.setFileUrl(ds.getValue(Rowitemroom.class).getFileUrl()); romchat.add(row); /* Rowitemroom row = snapshot.getValue(Rowitemroom.class); String caption = row.getCaption(); String url = row.getFileUrl();*/ } if (romchat.size()>0){ adapter = new CustomRoom(context,romchat); recyclerView.setAdapter(adapter); }else { Toast.makeText(context, "No data", Toast.LENGTH_SHORT).show(); } } db_url ="your apps`enter code here`.appspot.com/admins"