Это относится к преобразованию между внутренним JSON-подобным представлением Firestore и POJO и обратно из него.
Из моих экспериментов (у которых не было времени прочитать код) это похоже на преобразованиеиз POJO в JSON-подобный код Firestore использует общедоступные методы получения.
Однако при преобразовании из JSON-подобного в POJO код Firestore, похоже, ищет только совпадающие имена полей.
Почему этоявляется проблемой
Чтобы поддерживать теги для объекта, логическая структура данных, которую следует использовать, - Set
, а не List
.Я могу написать:
private Set<String> tags = new HashedSet<>();
public ImmutableList<String> getTags() {
ImmutableList.copyOf(this.tags);
}
Это прекрасно работает при сохранении моего POJO в Firestore, преобразовывая мой (внутренний) набор тегов в массив в Firestore.
Однако при чтении этого документав мой POJO (DocumentSnapshot.toObject()
) Firestore жалуется, что массивы можно десериализовать только до List
, а не любого другого типа Collection
.
Я попытался добавить:
private void setTags(List<String> tags) { //public method gives the same result
this.tags.clear();
this.tags.addAll(tags);
}
который теоретически должен был разрешить преобразование массива в JSON-подобных данных в мой POJO, но этот метод никогда не используется.Вместо этого я получаю следующее исключение:
Caused by: java.lang.RuntimeException: Could not deserialize object. Collections are not supported, please use Lists instead (found in field 'tags')
at com.google.cloud.firestore.CustomClassMapper.deserializeError(CustomClassMapper.java:524)
at com.google.cloud.firestore.CustomClassMapper.deserializeToParameterizedType(CustomClassMapper.java:280)
at com.google.cloud.firestore.CustomClassMapper.deserializeToType(CustomClassMapper.java:178)
at com.google.cloud.firestore.CustomClassMapper.access$200(CustomClassMapper.java:50)
at com.google.cloud.firestore.CustomClassMapper$BeanMapper.deserialize(CustomClassMapper.java:714)
at com.google.cloud.firestore.CustomClassMapper$BeanMapper.deserialize(CustomClassMapper.java:674)
at com.google.cloud.firestore.CustomClassMapper.convertBean(CustomClassMapper.java:503)
at com.google.cloud.firestore.CustomClassMapper.deserializeToClass(CustomClassMapper.java:242)
at com.google.cloud.firestore.CustomClassMapper.convertToCustomClass(CustomClassMapper.java:96)
at com.google.cloud.firestore.DocumentSnapshot.toObject(DocumentSnapshot.java:234)
at com.example.backend.data.FirestoreRepository.setId(FirestoreRepository.java:277)
at com.example.backend.data.FirestoreRepository.get(FirestoreRepository.java:124)
at com.example.backend.data.FirestoreRepository.get(FirestoreRepository.java:169)
at com.example.backend.web.pojos.Test.getQuestionById(Test.java:129)
... 51 more
Мой текущий обходной путь - иметь как List
, так и Set
внутри, а затем преобразовать из List
в Set
после загрузки данных.Это работает, но очень громоздко и подвержено ошибкам (легко допустить глупые ошибки программирования).Представьте себе, что вам нужно выполнить этот маленький танец для 10 различных объектов и возможность появления непреднамеренных ошибок.
Использование Firebase Admin SDK версии 6.3.0.
Некоторые тестовые коды и тестовые POJO
Вот базовый POJO, с которым я снова протестировал:
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import com.google.common.collect.ImmutableList;
public class TestQ {
private String id;
private Set<String> topicTags = new TreeSet<>();
public String getId() {
return this.id;
}
public ImmutableList<String> getTopicTags() {
return ImmutableList.copyOf(this.topicTags);
}
public TestQ setId(String id) {
this.id = id;
return this;
}
public TestQ setTopicTags(List<String> topicTags) {
this.topicTags.clear();
this.topicTags.addAll(topicTags);
return this;
}
}
Тестовый код, который пытается создать экземпляр этого класса из документа Firestore:
public void getTestQById(HttpServletRequest req, HttpServletResponse res, List<String> pathParts)
throws IOException {
String id = req.getParameter("id");
DocumentSnapshot ds;
try {
ds = db().collection("questions").document(id).get().get();
TestQ question = ds.toObject(TestQ.class);
PrintWriter writer = res.getWriter();
if (null != question) {
question.setId(id);
}
} catch (InterruptedException | ExecutionException | RuntimeException e) {
log().log(Level.WARNING, "Unexpected error", e);
}
}
Обратите внимание, что многие изсвойства документа Firestore были исключены из TestQ
POJO.Это показано в виде предупреждений ниже:
Sep 18, 2018 10:19:06 AM com.google.cloud.firestore.CustomClassMapper$BeanMapper deserialize
WARNING: No setter/field for approved found on class com.example.backend.model.questions.TestQ
Sep 18, 2018 10:19:06 AM com.google.cloud.firestore.CustomClassMapper$BeanMapper deserialize
WARNING: No setter/field for dateLastModified found on class com.example.backend.model.questions.TestQ
Sep 18, 2018 10:19:06 AM com.google.cloud.firestore.CustomClassMapper$BeanMapper deserialize
WARNING: No setter/field for dateCreated found on class com.example.backend.model.questions.TestQ
Sep 18, 2018 10:19:06 AM com.google.cloud.firestore.CustomClassMapper$BeanMapper deserialize
WARNING: No setter/field for level found on class com.example.backend.model.questions.TestQ
Sep 18, 2018 10:19:06 AM com.google.cloud.firestore.CustomClassMapper$BeanMapper deserialize
WARNING: No setter/field for options found on class com.example.backend.model.questions.TestQ
Sep 18, 2018 10:19:06 AM com.google.cloud.firestore.CustomClassMapper$BeanMapper deserialize
WARNING: No setter/field for approvedBy found on class com.example.backend.model.questions.TestQ
Sep 18, 2018 10:19:06 AM com.google.cloud.firestore.CustomClassMapper$BeanMapper deserialize
WARNING: No setter/field for text found on class com.example.backend.model.questions.TestQ
Sep 18, 2018 10:19:06 AM com.google.cloud.firestore.CustomClassMapper$BeanMapper deserialize
WARNING: No setter/field for lang found on class com.example.backend.model.questions.TestQ
Sep 18, 2018 10:19:06 AM com.example.backend.web.pojos.Test getTestQById
WARNING: Unexpected error
java.lang.RuntimeException: Could not deserialize object. Collections are not supported, please use Lists instead (found in field 'topicTags')
at com.google.cloud.firestore.CustomClassMapper.deserializeError(CustomClassMapper.java:524)
at com.google.cloud.firestore.CustomClassMapper.deserializeToParameterizedType(CustomClassMapper.java:280)
at com.google.cloud.firestore.CustomClassMapper.deserializeToType(CustomClassMapper.java:178)
at com.google.cloud.firestore.CustomClassMapper.access$200(CustomClassMapper.java:50)
at com.google.cloud.firestore.CustomClassMapper$BeanMapper.deserialize(CustomClassMapper.java:714)
at com.google.cloud.firestore.CustomClassMapper$BeanMapper.deserialize(CustomClassMapper.java:674)
at com.google.cloud.firestore.CustomClassMapper.convertBean(CustomClassMapper.java:503)
at com.google.cloud.firestore.CustomClassMapper.deserializeToClass(CustomClassMapper.java:242)
at com.google.cloud.firestore.CustomClassMapper.convertToCustomClass(CustomClassMapper.java:96)
at com.google.cloud.firestore.DocumentSnapshot.toObject(DocumentSnapshot.java:234)
at com.example.backend.web.pojos.Test.getTestQById(Test.java:146)
Соответствующая часть документа Firestore выглядит следующим образом:
![topicTags property in Firestore document](https://i.stack.imgur.com/gQJMr.png)