web-dev-qa-db-fra.com

Pourquoi est-ce que je reçois le message "Échec de l'envoi en mode reprise" lorsque je convertis du JSON de Firebase en objets Java?

[Divulgation: je suis un ingénieur à Firebase. Cette question est censée être une question de référence pour répondre à plusieurs questions à la fois.]

J'ai la structure JSON suivante dans ma base de données Firebase:

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

Je lis ceci avec le code suivant:

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

Mais je reçois cette erreur:

Exception dans le fil "FirebaseEventTarget" com.firebase.client.FirebaseException: Échec de l'envoi du type

Comment puis-je lire mes utilisateurs dans des objets Java?

37
Frank van Puffelen

Firebase utilise Jackson pour permettre la sérialisation d'objets Java en JSON et la désérialisation de JSON en objets Java. Vous pouvez en savoir plus sur Jackson sur le site Web de Jackson et cette page sur les annotations de Jackson

Dans la suite de cette réponse, nous allons montrer quelques façons courantes d’utiliser Jackson avec Firebase.

Chargement d'utilisateurs complets

Le moyen le plus simple de charger les utilisateurs de Firebase dans Android est de créer une classe Java qui imite complètement les propriétés du 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+"\’}”; }
}

Nous pouvons utiliser cette classe dans un auditeur:

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

Vous pouvez noter que la classe User suit le modèle de propriété JavaBean . Chaque propriété JSON est mappée par un champ de la classe User et nous avons une méthode de lecture publique pour chaque champ. En nous assurant que toutes les propriétés sont mappées avec le même nom, nous nous assurons que Jackson puisse les mapper automatiquement.

Vous pouvez également contrôler manuellement le mappage en mettant des annotations de Jackson sur votre classe Java, ses champs et ses méthodes. Nous allons couvrir les deux annotations les plus courantes (@JsonIgnore et @JsonIgnoreProperties) ci-dessous.

Chargement partiel des utilisateurs

Dites que vous ne vous souciez que du nom de l'utilisateur et que vous le manipulez dans votre code Java. Supprimons la stackId et voyons ce qui se passe:

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

Si nous attachons maintenant le même écouteur qu'auparavant et exécutons le programme, une exception sera générée:

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)

Le «type d’échec au traitement anti-rebond» indique que Jackson n’a pas pu désérialiser le JSON en objet User. Dans l'exception imbriquée, il nous explique pourquoi:

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 a trouvé une propriété stackId dans le JSON et ne sait pas quoi en faire, alors il lève une exception. Heureusement, nous pouvons utiliser une annotation pour lui dire d'ignorer des propriétés spécifiques du JSON lors de son mappage vers notre classe User:

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

Si nous n'exécutons pas le code avec notre écouteur à nouveau, Jackson saura qu'il peut ignorer stackId dans le JSON et qu'il sera capable de désérialiser le JSON dans un objet Utilisateur.

L'ajout de propriétés au JSON étant une pratique courante dans les applications Firebase, il peut être plus pratique de simplement dire à Jackson d'ignorer toutes les propriétés qui ne possèdent pas de mappage dans la classe Java:

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

Maintenant, si nous ajoutons des propriétés au JSON ultérieurement, le code Java pourra toujours charger le Users. N'oubliez pas que les objets Utilisateur ne contiennent pas toutes les informations présentes dans le JSON. Soyez donc prudent lorsque vous les écrivez à nouveau dans Firebase.

Sauver partiellement les utilisateurs

L'une des raisons pour lesquelles il est agréable d'avoir une classe Java personnalisée est que nous pouvons y ajouter des méthodes pratiques. Supposons que nous ajoutions une méthode pratique qui donne le nom à afficher pour un utilisateur:

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

Lisons maintenant les utilisateurs de Firebase et réécrivons-les dans un nouvel emplacement:

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

Le JSON dans le noeud copiedusers ressemble à ceci:

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

Ce n'est pas la même chose que le JSON source, car Jackson reconnaît la nouvelle méthode getDisplayName() en tant que getter JavaBean et a donc ajouté une propriété displayName au JSON qu'il génère. Nous résolvons ce problème en ajoutant une annotation JsonIgnore à getDisplayName().

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

Lors de la sérialisation d'un objet User, Jackson ignorera maintenant la méthode getDisplayName() et le JSON que nous écrivons sera le même que celui que nous avons obtenu.

49
Frank van Puffelen

Les versions 9.x (et supérieures) du SDK Firebase pour Android/Java ont été arrêtées, y compris Jackson pour la sérialisation/désérialisation de Java <-> JSON. Le kit de développement logiciel plus récent fournit à la place un ensemble minimal d’annotations personnalisées permettant de contrôler les besoins de personnalisation les plus courants, tout en ayant un impact minimal sur la taille du fichier JAR/APK obtenu.


Ma réponse originale est toujours valable si vous êtes:

Le reste de cette réponse explique comment gérer les scénarios de sérialisation/désérialisation dans Firebase SDK 9.0 ou version ultérieure.


Structure de données

Nous allons commencer par cette structure JSON dans notre base de données Firebase:

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

Chargement d'utilisateurs complets

À la base, nous pouvons charger chaque utilisateur de ce JSON dans la classe Java suivante:

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

Si nous déclarons les champs publics, nous n'avons même pas besoin des getters:

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

Chargement partiel des utilisateurs

Nous pouvons également charger partiellement un utilisateur, par exemple avec:

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

Lorsque nous utilisons cette classe pour charger les utilisateurs à partir du même JSON, le code est exécuté (contrairement à la variante de Jackson mentionnée dans mon autre réponse). Mais vous verrez un avertissement dans votre sortie de journalisation:

AVERTISSEMENT: aucun identifiant/champ pour stackId trouvé dans la classe Annotations $ PartialUser

Alors débarrassez-vous de cela, nous pouvons annoter la classe avec @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 + "''}";
    }
}

Sauver partiellement les utilisateurs

Comme auparavant, vous souhaiterez peut-être ajouter une propriété calculée à l'utilisateur. Vous voudriez ignorer une telle propriété lors de la sauvegarde des données dans la base de données. Pour ce faire, vous pouvez annoter la propriété/getter/setter/field avec @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+ "'}"; }
}

Désormais, lors de l'écriture d'un utilisateur dans la base de données, la valeur de getTag() sera ignorée.

Utilisation d'un nom de propriété différent dans le code JSON par rapport au code Java

Vous pouvez également spécifier le nom qu'un champ/getter/setter du code Java doit obtenir dans le code JSON de la base de données. Pour ce faire: annotez le champ/getter/setter avec @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+ "'}"; }
}

En général, il est préférable d’utiliser le mappage par défaut entre Java <-> JSON utilisé par le SDK Firebase. Cependant, @PropertyName peut être nécessaire lorsque vous avez une structure JSON préexistante que vous ne pouvez pas mapper autrement à des classes Java.

3
Frank van Puffelen

Parce que votre mauvais dossier de chemin de requête dans root.this est example  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