为什么我得到"未能反弹键入"当我打开JSON从火力地堡成Java对象?地堡、当我、火力、对象

2023-09-05 00:24:42 作者:听西园夜雨

[披露:我是一个工程师火力地堡。这个问题,就是要参考问题一气呵成回答很多问题。] 的

我在我的火力地堡数据库以下JSON结构:

I have the following JSON structure in my Firebase database:

{  
  "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
    }
  }
}

我读这与以下code:

I am reading this with the following code:

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) { }
});

不过,我得到这个错误:

But I get this error:

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

我怎么能读我的用户为Java对象?

How can I read my users into Java objects?

推荐答案

火力地堡使用杰克逊允许Java序列化对象到JSON和JSON反序列化回Java对象。你可以找到更多有关杰克逊杰克逊网站这的有关杰克逊注释页。

Firebase uses Jackson to allow serialization of Java objects to JSON and deserialization of JSON back into Java objects. You can find more about Jackson on the Jackson website and this page about Jackson annotations.

在这个答案的其余部分,我们将展示使用杰克逊火力地堡的一些常用方法。

In the rest of this answer, we’ll show a few common ways of using Jackson with Firebase.

加载用户火力地堡到Android的最简单的方法是,如果我们创建一个Java类,完全模仿了JSON的属性:

The simplest way of loading the users from Firebase into Android is if we create a Java class that completely mimics the properties in the JSON:

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

我们可以在一个监听器使用这个类:

We can use this class in a listener:

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财产在用户类中的字段映射,我们有一个公共的getter方法​​为每个字段。通过确保所有属性都映射到完全相同的名称,我们保证杰克逊可以自动映射。

You may note that the User class follow the JavaBean property pattern. Every JSON property maps by a field in the User class and we have a public getter method for each field. By ensuring that all properties are mapped with the exact same name, we ensure that Jackson can automatically map them.

您也可以手动将杰克逊注解Java类控制映射,其字段和方法。我们将介绍最常见的两种注解( @JsonIgnore @JsonIgnoreProperties )以下。

You can also manually control the mapping by putting Jackson annotations on your Java class, and its fields and methods. We’ll cover the two most common annotations (@JsonIgnore and @JsonIgnoreProperties) below.

你说,你只关心用户的名字和你的Java code处理。让我们删除 stackId 键,看看会发生什么:

Say that you only care about the user’s name and handle in your Java code. Let’s remove the stackId and see what happens:

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

如果我们现在像以前那样重视同监听器和运行程序,它会抛出一个异常:

If we now attach the same listener as before and run the program, it will throw an 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)

无法抖型表示,杰克逊无法反序列化JSON到一个User对象。在嵌套异常,它告诉我们原因:

The "failed to debounce type" indicates that Jackson was unable to deserialize the JSON into a User object. In the nested exception it tells us why:

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)

杰克逊发现了JSON的属性 stackId ,不知道该怎么做,所以它抛出一个异常。幸运的是,我们可以用它来告诉它从JSON时,它映射到我们的用户忽略的特定属性注释类:

Jackson found a property stackId in the JSON and doesn’t know what to do with it, so it throws an exception. Luckily there is an annotation that we can use to tell it to ignore specific properties from the JSON when mapping it to our User class:

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

如果我们没有运行code我们再次聆听者,杰克逊会知道它可以忽略 stackId 在JSON,这将是能够反序列化的JSON成User对象了。

If we not run the code with our listener again, Jackson will know that it can ignore stackId in the JSON and it will be able to deserialize the JSON into a User object again.

由于添加属性的JSON是在火力地堡的应用,一种普遍的做法,你会发现它更方便简单地告诉杰克逊忽略不具备的Java类的映射关系的所有属性:

Since adding properties to the JSON is such a common practice in Firebase applications, you may find it more convenient to simply tell Jackson to ignore all properties that don’t have a mapping in the Java class:

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

现在,如果我们添加属性的JSON后,Java的code将仍然能够装载用户秒。只要记住,用户对象将不包含为present在JSON的所有信息,所以写他们的时候回到火力地堡再小心。

Now if we add properties to the JSON later, the Java code will still be able to load the Users. Just keep in mind that the User objects won’t contain all information that was present in the JSON, so be careful when writing them back to Firebase again.

原因之一,它是好的,有一个自定义的Java类,就是我们可以添加方便的方法吧。再说说我们添加,获取用来显示用户名的简便方法:

One reason why it is nice to have a custom Java class, is that we can add convenience methods to it. Say that we add a convenience method that gets the name to display for a user:

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

现在,让我们读出火力地堡的用户,并把它们写回到一个新的位置:

Now let's read the users from Firebase and write them back into a new location:

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 节点看起来是这样的:

The JSON in the copiedusers node looks like this:

"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和由此增加了一个显示名属性,它输出的JSON。我们通过增加一个 JsonIgnore 注释 getDisplayName()

That’s not the same as the source JSON, because Jackson recognizes the new getDisplayName() method as a JavaBean getter and thus added a displayName property to the JSON it outputs. We solve this problem by adding a JsonIgnore annotation to getDisplayName().

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

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

When serializing a User object, Jackson will now ignore the getDisplayName() method and the JSON we write out will be the same as what we got it.