无法使用Gson库对Firestore DocumentReference数据类型进行序列化/反序列化

时间:2018-06-26 22:48:43

标签: java android firebase gson google-cloud-firestore

当尝试使用DocumentReference库对Firestore数据库中的Gson数据类型进行序列化/反序列化时,遇到以下异常。我正在使用通过模拟器运行的Google Play服务插件(版本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);
}

上面的serialize方法中发生异常。在过去的几天里,我已经研究了这个问题,但尚未发现任何有用的东西。由于GeoPoint和Timestamp数据类型(它们均由简单的原语组成)不会发生此异常,因此可能是由于DocumentReference类的属性引起的。上面的代码对于我在项目中拥有的其他实体(任何没有Reference数据类型的实体)均正常工作。任何帮助,将不胜感激。谢谢。

解决方案:

与其对序列本身进行序列化/反序列化,请通过在引用上使用getPath()方法将其序列化为字符串,然后返回通过使用FirebaseFirestore.getInstance()。document()创建的DocumentReference对其进行序列化。这是执行此操作的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 :(得分:1)

不要尝试直接序列化DocumentReference。您需要序列化的DocumenetReference中唯一重要的数据是路径字符串。如果将其序列化,则可以根据需要重新构造DocumentReference对象。

DocumentReference ref = ...
String path = ref.getPath();
DocumentReference ref2 = FirebaseFirestore.getInstance().document(path);
// Now ref and ref2 point to the same document