Подтвердить что ты не робот

Почему я получаю сообщение "Failed to bounce to type", когда я включаю JSON из Firebase в объекты Java?

[Раскрытие информации: Я инженер в Firebase. Этот вопрос призван стать справочным вопросом для ответа на многие вопросы за один раз.]

У меня есть следующая структура JSON в моей базе данных 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
    }
  }
}

Я читаю это со следующим кодом:

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?

4b9b3361

Ответ 1

Firebase использует Jackson для разрешения сериализации объектов Java на JSON и десериализации JSON обратно в объекты Java. Вы можете найти больше о Джексоне на веб-сайте Джексона и этой странице о аннотации Джексона.

В оставшейся части этого ответа хорошо продемонстрируйте несколько общих способов использования Джексона с Firebase.

Загрузка завершенных пользователей

Самый простой способ загрузки пользователей из Firebase в Android - это создать класс Java, который полностью имитирует свойства в 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+"\’}"; }
}

Мы можем использовать этот класс в слушателе:

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 для каждого поля. Обеспечивая сопоставление всех свойств с одним и тем же именем, мы гарантируем, что Джексон может автоматически их сопоставить.

Вы также можете вручную управлять отображением, помещая аннотации Джексона на свой 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)

"Не удалось тип debounce" указывает, что Джексон не смог десериализовать 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 и не знает, что с ним делать, поэтому он генерирует исключение. К счастью, есть аннотация, которую мы можем использовать, чтобы сообщить ему игнорировать конкретные свойства из JSON при сопоставлении его с нашим классом User:

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

Если мы не снова запустим код с нашим слушателем, Джексон узнает, что он может игнорировать stackId в JSON, и он сможет снова десериализовать JSON в объект User.

Поскольку добавление свойств JSON является такой распространенной практикой в ​​приложениях Firebase, вам может быть удобнее просто сказать Джексону игнорировать все свойства, которые не имеют сопоставления в классе Java:

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

Теперь, если мы добавим свойства в JSON позже, код Java все равно сможет загрузить User s. Просто имейте в виду, что объекты 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) { }
});

JSON в copiedusers node выглядит следующим образом:

"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 и, таким образом, добавляет свойство displayName к выходу JSON. Мы решим эту проблему, добавляя аннотацию JsonIgnore к getDisplayName().

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

При сериализации объекта User Джексон теперь проигнорирует метод getDisplayName(), и JSON, который мы выпишем, будет таким же, как и у нас.

Ответ 2

9.x (и более высокие) версии Firebase SDK для Android/Java перестали включать Jackson для сериализации/десериализации Java <-> JSON. Новый SDK вместо этого предоставляет минимальный набор пользовательских аннотаций, позволяющих управлять наиболее распространенными потребностями в настройке, при этом минимально влияя на размер JAR/APK.


Мой первоначальный ответ по-прежнему действителен, если вы:

Остальная часть этого ответа описывает, как обрабатывать сценарии сериализации/десериализации в Firebase SDK 9.0 или выше.


Структура данных

Мы начнем с этой структуры JSON в нашей базе данных 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
  }
}

Загрузка завершенных пользователей

В самом основном, мы можем загрузить каждого пользователя из этого 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, код запускается (в отличие от варианта Джексона, упомянутого в моем другом ответе). Но вы увидите предупреждение в своем журнале:

ПРЕДУПРЕЖДЕНИЕ: нет setter/field для stackId, найденного в классе Аннотации $ 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 + "''}";
    }
}

Частичное сохранение пользователей

Как и раньше, вы можете захотеть добавить вычисленное свойство пользователю. Вы хотите игнорировать такое свойство при сохранении данных обратно в базу данных. Для этого вы можете аннотировать свойство /getter/setter/field с помощью @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() будет проигнорировано.

Использование другого имени свойства в JSON, чем в Java-коде

Вы также можете указать, какое имя поле /getter/setter из кода 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+ "'}"; }
}

В общем случае лучше использовать сопоставление по умолчанию между Java <-> JSON, которое использует Firebase SDK. Но @PropertyName может понадобиться, если у вас есть уже существующая структура JSON, которую вы иначе не можете сопоставить с Java-классами.

Ответ 3

Потому что ваша неправильная папка пути запроса в root.this является примером введите описание изображения здесь

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"