Ответ 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, который мы выпишем, будет таким же, как и у нас.