2015-08-20 18 views
36

[披露:我是Firebase的工程师。这个问题,就是要参考的问题回答很多问题一气呵成]当我将Firebase中的JSON转换为Java对象时,为什么我会收到“无法弹出键入”?

我有以下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) { } 
}); 

但我得到这个错误:

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

如何将用户读入Java对象?

+0

您如何看待这种方法? stackoverflow.com/a/36706248/2819864 – RominaV

回答

47

Firebase使用Jackson将Java对象序列化为JSON并将JSON反序列化为Java对象。你可以在the Jackson website和这个page about Jackson annotations找到更多关于杰克逊的信息。

在本答案的其余部分中,我们将介绍几种在Firebase中使用Jackson的常用方法。

加载完整的用户

从火力地堡加载用户进入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 property pattern。每个JSON属性都由User类中的一个字段映射,并且每个字段都有一个公共的getter方法。通过确保所有属性都使用完全相同的名称进行映射,我们确保杰克逊可以自动映射它们。

您还可以通过将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 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: [email protected]; 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,不知道该怎么用它做,所以它抛出一个异常。幸运的是,我们可以用它来告诉它映射到我们的User上课的时候从JSON忽略特定属性的注释:

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

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

由于添加属性到JSON在火力地堡应用,例如常见的做法,你会发现它更方便简单地告诉杰克逊忽略不具有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 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) { } 
}); 

的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()方法作为JavaB ean getter,从而将displayName属性添加到它输出的JSON。我们通过将JsonIgnore注释添加到getDisplayName()来解决此问题。

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

当序列化用户对象,杰克逊现在忽略getDisplayName()方法和JSON,我们写出来的将是相同的是我们得到了它。

+2

我对你的回答中的严谨感到兴奋,尤其是使用短语“我们”。继续保持下去 –

+6

我想添加它也可以通过JsonHelpers.getMapper()。configure(DeserializationFeature)来访问Jackson ObjectMapper。FAIL_ON_UNKNOWN_PROPERTIES,false);',并且您可以以这种方式禁用此错误 - 也许如果您无权访问您的类。 –

+0

很酷,我不知道。谢谢托尼! –

0

因为root.this你的不对了查询路径文件夹例如 enter image description here

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" 
+1

感谢您的答案@YtgDoc。我不是很确定我明白它是如何与问题相关的,但你能解释一下吗? –

+0

我不得不问你同样的问题,调试器说错误“无法弹出键入“,因为行后面的孩子只是在根后面的”人“一级,它只是能够在后面的行后面运行孩子时根后面的”人“两级:)。对不起,我的英文技能很差 –

3

的9.x中(或更高版本)的火力地堡SDK为Android/Java的版本中停止包括杰克逊序列化/反序列化的Java < - > JSON。较新的SDK改为提供最少量的自定义注释,以控制最常见的自定义需求,同时对所产生的JAR/APK大小的影响最小。


My original answer仍然是有效的,如果你是:

本答案的其余部分将介绍如何处理Firebase SDK 9.0或更高版本中的序列化/反序列化方案。


数据结构

我们将与我们的火力地堡数据库这个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 
    } 
} 

完全加载用户

在最基本的,我们可以加载每个用户从此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变体)。但你会在你的日志输出看到一个警告:

WARNING: No setter/field for stackId found on class Annotations$PartialUser

因此摆脱这一点,我们可以注释类@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 /场:写用户数据库时

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+ "'}"; } 
} 

一般来说,最好使用Java <之间的默认映射 - 即火力地堡SDK使用> JSON。但是当您有一个预先存在的JSON结构时,可能需要@PropertyName,否则您无法映射到Java类。

相关问题