web-dev-qa-db-ja.com

FirebaseのJSONをJavaオブジェクトに変換すると、「入力にバウンスできませんでした」というメッセージが表示されるのはなぜですか?

[開示:私は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オブジェクトに読み込むにはどうすればよいですか?

41

FirebaseはJacksonを使用してJavaオブジェクトをJSONにシリアル化し、JSONをJavaオブジェクトに逆シリアル化します。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アノテーションを配置することにより、マッピングを手動で制御することもできます。最も一般的な2つのアノテーション(_@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をユーザーオブジェクトにデシリアライズできなかったことを示します。ネストされた例外では、次の理由がわかります。

_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を見つけましたが、それをどう処理するかわからないため、例外をスローします。幸いなことに、UserクラスにマッピングするときにJSONの特定のプロパティを無視するように指示するために使用できる注釈があります。

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

リスナーを使用してコードを再度実行しない場合、JacksonはJSONのstackIdを無視できることを認識し、JSONをユーザーオブジェクトにデシリアライズできるようになります。

Firebaseアプリケーションでは、プロパティをJSONに追加するのが一般的であるため、Javaクラスにマッピングのないすべてのプロパティを無視するようにジャクソンに指示する方が便利な場合があります。

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

後でJSONにプロパティを追加した場合、JavaコードはUsersをロードできます。ユーザーオブジェクトにはすべての情報が含まれないことに注意してください。 JSONに存在していたので、Firebaseに再度書き込む際には注意してください。

ユーザーを部分的に保存する

カスタムのJavaクラスを用意するのが良い理由の1つは、便利なメソッドを追加できることです。ユーザーに表示する名前を取得する便利なメソッドを追加するとします。

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

Jacksonは新しいgetDisplayName()メソッドをJavaBeanゲッターとして認識し、出力するJSONにdisplayNameプロパティを追加したため、ソースJSONとは異なります。 JsonIgnore注釈をgetDisplayName()に追加することにより、この問題を解決します。

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

Userオブジェクトをシリアル化するとき、JacksonはgetDisplayName()メソッドを無視するようになり、書き出すJSONは取得したものと同じになります。

50

Firebase SDK for Android/Javaの9.x(およびそれ以降)バージョンは、Java <-> JSONのシリアル化/非シリアル化のためのJacksonを含めなくなりました。代わりに、新しいSDKは最小限のカスタムアノテーションセットを提供して、最も一般的なカスタマイズニーズを制御できるようにしますが、結果のJAR/APKサイズへの影響は最小限に抑えます。


私の元の答え は次の場合に有効です:

この回答の残りの部分では、Firebase SDK 9.0以降でのシリアル化/逆シリアル化のシナリオの処理方法について説明します。


データ構造

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
  }
}
_

完全なユーザーの読み込み

最も基本的には、この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のセッター/フィールドが見つかりません

それを取り除くために、クラスに_@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_の注釈を付けることができます。

_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()の値は無視されます。

JavaコードではなくJSONで異なるプロパティ名を使用する

また、JavaコードがデータベースのJSONで取得する必要があるから、フィールド/ゲッター/セッターの名前を指定することもできます。これを行うには、フィールド/ゲッター/セッターに@PropertyName()

_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間のデフォルトマッピングを使用するのが最善です。ただし、Javaクラスにマップできない既存のJSON構造がある場合は、_@PropertyName_が必要になる場合があります。

4

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"
0
Chung Nguyen