简体   繁体   English

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

[英]Firestore DocumentReference data type can't be serialized/deserialized using the Gson library

I am experiencing the following exception when attempting to serialize/deserialize the DocumentReference data type in a Firestore database using the Gson library. 尝试使用Gson库在Firestore数据库中序列化/反序列化DocumentReference数据类型时,遇到以下异常。 I am using google play services plugin (version 4.0.1), the gson library (version 2.8.5), and a Nexus 5X API 25 (Android 7.1.1 (Google Play)) virtual device running through the emulator. 我正在使用通过模拟器运行的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

I am able to to retrieve the DocumentReference correctly and cast it to my ReferenceEntity class using the following code. 我能够使用以下代码正确检索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);

                ...
            });
}

Here is the ReferenceEntity class and the BaseEntity class files (@SerializedName annotation is for gson, @PropertyName annotation is for Firestore): 这是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;
    }
}

The above code works since the print statement correctly spits out: 上面的代码可以正常工作,因为print语句可以正确弹出:

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

This is the caching method and the serialize method: 这是缓存方法和序列化方法:

@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);
}

The exception occurs in the serialize method above. 上面的序列化方法中发生了异常。 I have researched this problem for the past few days and have not yet found anything useful. 在过去的几天里,我已经研究了这个问题,但尚未发现任何有用的东西。 Since this exception doesn't occur for the GeoPoint and Timestamp data types (which are both composed of simple primitives I think), it may be caused due to the properties of the DocumentReference class. 由于GeoPoint和Timestamp数据类型(它们均由简单的原语组成)不会发生此异常,因此可能是由于DocumentReference类的属性引起的。 The code above is working correctly for the other entities that I have in the project (any that do not have a Reference data type). 上面的代码对于我在项目中拥有的其他实体(任何没有Reference数据类型的实体)均正常工作。 Any help would be appreciated. 任何帮助,将不胜感激。 Thank you. 谢谢。

Solution: 解:

Instead of serializing/deserializing the Reference itself, serialize it as a string by using the getPath() method on the reference and deserialize it by returning a DocumentReference created using FirebaseFirestore.getInstance().document(). 不用序列化/反序列化引用本身,而是通过在引用上使用getPath()方法将其序列化为字符串,然后返回通过使用FirebaseFirestore.getInstance()。document()创建的DocumentReference对其进行反序列化。 Here is the full code for the Serializer class that does this: 这是执行此操作的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);
    }
}

Don't try to serialize a DocumentReference directly. 不要尝试直接序列化DocumentReference。 The only important piece of data in a DocumenetReference that you would need to serialize is the path string. 您需要序列化的DocumenetReference中唯一重要的数据是路径字符串。 If you serialize that, you can reconstitute a DocumentReference object, if necessary. 如果对此进行序列化,则可以根据需要重新构造DocumentReference对象。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM