Тип данных Firestore DocumentReference нельзя сериализовать / десериализовать с помощью библиотеки Gson - PullRequest
0 голосов
/ 27 июня 2018

При попытке сериализации / десериализации типа данных DocumentReference в базе данных Firestore с использованием библиотеки Gson возникает следующее исключение. Я использую плагин Google Play Services (версия 4.0.1), библиотеку gson (версия 2.8.5) и виртуальное устройство Nexus 5X API 25 (Android 7.1.1 (Google Play)), работающее через эмулятор.

W/System.err: java.lang.AssertionError: impossible
D/FA: Logging event (FE): session_start(_s), Bundle[{firebase_event_origin(_o)=auto, firebase_screen_class(_sc)=MainActivity, firebase_screen_id(_si)=-2086822989708624881}]
W/System.err:     at java.lang.Enum$1.create(Enum.java:269)
                  at java.lang.Enum$1.create(Enum.java:260)
                  at libcore.util.BasicLruCache.get(BasicLruCache.java:58)
                  at java.lang.Enum.getSharedConstants(Enum.java:286)
                  at java.lang.Class.getEnumConstantsShared(Class.java:2291)
W/System.err:     at java.lang.Class.getEnumConstants(Class.java:2279)
                  at com.google.gson.internal.bind.TypeAdapters$EnumTypeAdapter.<init>(TypeAdapters.java:779)
                  at com.google.gson.internal.bind.TypeAdapters$30.create(TypeAdapters.java:818)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
W/System.err:     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.internal.bind.CollectionTypeAdapterFactory.create(CollectionTypeAdapterFactory.java:53)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
W/System.err:     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.Gson.toJson(Gson.java:696)
                  at com.google.gson.Gson.toJson(Gson.java:683)
                  at com.google.gson.Gson.toJson(Gson.java:638)
                  at com.involveunation.involveu.data.cache.Serializer.serialize(Serializer.java:28)
W/System.err:     at com.involveunation.involveu.data.cache.CacheImpl.put(CacheImpl.java:73)
                  at com.involveunation.involveu.data.repository.feed.FeedCloudDataStore.lambda$getFeedEntityData$2$FeedCloudDataStore(FeedCloudDataStore.java:57)
                  at com.involveunation.involveu.data.repository.feed.FeedCloudDataStore$$Lambda$0.apply(Unknown Source)
                  at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.onNext(ObservableFlatMap.java:121)
                  at io.reactivex.internal.operators.observable.ObservableCreate$CreateEmitter.onNext(ObservableCreate.java:67)
                  at com.involveunation.involveu.data.network.FirebaseApiImplementation.lambda$null$0$FirebaseApiImplementation(FirebaseApiImplementation.java:74)
                  at com.involveunation.involveu.data.network.FirebaseApiImplementation$$Lambda$9.onComplete(Unknown Source)
                  at com.google.android.gms.tasks.zzj.run(Unknown Source)
                  at android.os.Handler.handleCallback(Handler.java:751)
                  at android.os.Handler.dispatchMessage(Handler.java:95)
                  at android.os.Looper.loop(Looper.java:154)
                  at android.app.ActivityThread.main(ActivityThread.java:6119)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
              Caused by: java.lang.NoSuchMethodException: values []
                  at java.lang.Class.getMethod(Class.java:1981)
                  at java.lang.Class.getDeclaredMethod(Class.java:1960)
                  at java.lang.Enum$1.create(Enum.java:265)
                ... 49 more

Я могу правильно извлечь DocumentReference и привести его к моему классу ReferenceEntity, используя следующий код.

@Override
public Observable<FeedEntity> getFeedEntityData(String id) {
    return this.firebaseApi.getDocument(this.firebaseFirestore.collection("feed").document(id))
            .flatMap((DocumentSnapshot response) -> {

                // Creates the ReferenceEntity by casting the result to class DocumentSnapshot.
                ReferenceEntity referenceEntity = response.toObject(ReferenceEntity.class);
                // Sets the id of the entity for getting it from the cache later.
                referenceEntity.setId("ThrowawayId");

                // Prints out the DocumentReference path from the database.
                System.out.println("ownerReference = " + referenceEntity.getOwnerReference().getPath());

                // Puts the newly made referenceEntity into the cache.
                FeedCloudDataStore.this.cache.put(referenceEntity, ReferenceEntity.class);

                // Gets the referenceEntity from the cache using its key (first parameter).
                // This never gets called due to the exception being thrown in the method above.
                FeedCloudDataStore.this.cache.get("ThrowawayId", ReferenceEntity.class);

                ...
            });
}

Вот класс ReferenceEntity и файлы класса BaseEntity (аннотация @SerializedName для gson, аннотация @PropertyName для Firestore):

import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.PropertyName;
import com.google.gson.annotations.SerializedName;

public class ReferenceEntity extends BaseEntity {

    public ReferenceEntity() {

    }

    @SerializedName("owner_ref")
    private DocumentReference ownerReference;

    @PropertyName("owner_ref")
    public DocumentReference getOwnerReference() {
        return ownerReference;
    }

    @PropertyName("owner_ref")
    public void setOwnerReference(DocumentReference ownerReference) {
        this.ownerReference = ownerReference;
    }
}

...

public abstract class BaseEntity {

    private String id;

    public String getId() {
        return this.id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

Приведенный выше код работает, поскольку оператор print правильно выплевывает:

I/System.out: ownerReference = organizations/-Kd2GYk3EN9YI4FwmLyu 

Это метод кэширования и метод сериализации:

@Override
public <T> void put(T entity, Class<T> clazz) {
    if (entity != null) {
        final File file = this.createFile(((BaseEntity) entity).getId());
        if (!isCached(((BaseEntity) entity).getId())) {
            final String json = this.serializer.serialize(entity, clazz);
            ...
        }
    }
}

...

public <T> String serialize(T entity, Class<T> clazz) {
    return gson.toJson(entity, clazz);
}

Исключение происходит в методе сериализации выше. Я исследовал эту проблему в течение последних нескольких дней и пока не нашел ничего полезного. Поскольку это исключение не возникает для типов данных GeoPoint и Timestamp (которые, как мне кажется, состоят из простых примитивов), оно может быть вызвано свойствами класса DocumentReference. Приведенный выше код работает правильно для других объектов, которые у меня есть в проекте (те, которые не имеют тип данных Reference). Любая помощь будет оценена. Спасибо.

Решение:

Вместо сериализации / десериализации самой ссылки, сериализуйте ее как строку, используя метод getPath () для ссылки, и десериализуйте ее, возвращая DocumentReference, созданный с помощью FirebaseFirestore.getInstance (). Document (). Вот полный код класса Serializer, который делает это:

import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializer;

import java.lang.reflect.Type;

import javax.inject.Inject;
import javax.inject.Singleton;

/**
 * Json Serializer/Deserializer.
 */
@Singleton
public class Serializer {

    private final Gson gson;

    @Inject
    Serializer() {
        // Custom serializer that accepts a DocumentReference data type and returns the
        // reference path as a string.
        JsonSerializer<DocumentReference> referenceSerializer = (src, type, context) ->
                src == null ? null : new JsonPrimitive(src.getPath());
        // Custom deserializer that accepts a json string for the DocumentReference data type and
        // returns a new DocumentReference that is created using the string reference path.
        JsonDeserializer<DocumentReference> referenceDeserializer = (JsonElement json, Type type,
                                                                     JsonDeserializationContext context) ->
                json == null ? null : FirebaseFirestore.getInstance().document(json.getAsString());
        // Builds the gson object using our custom DocumentReference serializer/deserializer above.
        gson = new GsonBuilder()
                .registerTypeAdapter(DocumentReference.class, referenceSerializer)
                .registerTypeAdapter(DocumentReference.class, referenceDeserializer).create();
    }

    /**
     * Serialize an object to Json.
     *
     * @param entity Object to serialize.
     * @param clazz Type of the entity to serialize.
     */
    public <T> String serialize(T entity, Class<T> clazz) {
        return gson.toJson(entity, clazz);
    }

    /**
     * Deserialize a json representation of an object.
     *
     * @param string Entity json string to deserialize.
     * @param clazz Type of the entity to deserialize.
     */
    public <T> T deserialize(String string, Class<T> clazz) {
        return gson.fromJson(string, clazz);
    }
}

1 Ответ

0 голосов
/ 27 июня 2018

Не пытайтесь сериализовать DocumentReference напрямую. Единственная важная часть данных в DocumenetReference, которую вам необходимо сериализовать, - это строка пути. Если вы сериализуете это, вы можете при необходимости восстановить объект DocumentReference.

DocumentReference ref = ...
String path = ref.getPath();
DocumentReference ref2 = FirebaseFirestore.getInstance().document(path);
// Now ref and ref2 point to the same document
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...