Firebase Cloud Firestore – Convert String to Java Enum

Den*_*tal 4 java enums android firebase google-cloud-firestore

I'm trying to get some data from my Cloud Firestore into my Android App, but I'm having problem with enums. I have saved a String in the Cloud Firestore for the value of the enum, but when I convert the DocumentSnaphot I receive to an object, the app crashes because it's trying to convert the String to an enum based on the enum name (which isn't the same as the value).

The error I get is(I'm sending the value "NLD"):

java.lang.RuntimeException: Could not deserialize object. Could not find enum value of nl.gemoro.lgs.enums.CountryCode for value "NLD" (found in field 'address.countryCode') 
Run Code Online (Sandbox Code Playgroud)

The enum looks like this:

public enum CountryCode {
    NETHERLANDS("NLD"),
    UNKNOWN("???");

    private final String value;

    CountryCode(String s) {
        value = s;
    }

    public boolean equalsValue(String otherValue) {
        return value.equals(otherValue);
    }

    public String toString() {
        return this.value;
    }
}
Run Code Online (Sandbox Code Playgroud)

I'm using this method to get the data from the Firestore and convert the DocumentSnapshot to the given class:

public static void getAllDocumentsConverted(String collection, final Class convertClass, final OperationCompletedListener listener) {
    FirebaseFirestore db = FirebaseFirestore.getInstance();
    db.collection(collection)
            .get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "Found " + task.getResult().size() + " documents");
                        List<Object> list = new ArrayList<>();
                        List<String> ids = new ArrayList<>();
                        for (DocumentSnapshot document : task.getResult()) {
                            list.add(document.toObject(convertClass));
                            ids.add(document.getId());
                        }
                        listener.onOperationComplete(Result.SUCCESS, list, ids);
                    } else {
                        Log.d(TAG, "Error getting documents: ", task.getException());
                        listener.onOperationComplete(Result.FAILED);
                    }
                }
            });
}
Run Code Online (Sandbox Code Playgroud)

I'm not sure if it's even people to get the result I want, but I would really like it if it did work some way.

EDIT: To be clear: I can convert the String to an enum if the enum just consists out of the enum names or if the names and values are the same.

Thanks in advance.

Ben*_*ane 5

枚举大小写需要精确匹配可能的String值,包括大小写。

例如,如果您所在国家/地区的值可以为"NLD""US",则枚举的结构应如下所示:

public enum CountryCode {
    NLD,
    US
}
Run Code Online (Sandbox Code Playgroud)

这样,Firebase可以自动将String转换为要转换为的模型的枚举。

注意:Firebase用于<YourEnumType>.valueOf("value-from-doc")尝试序列化为枚举。您无法valueOf在Java中覆盖枚举,因此这是我们目前最好的将String序列化为枚举的方法。


话虽这么说,如果您这样做的话,如果您收到的值与您的任何枚举值都不匹配,则您将面临异常。相反,您可以使用Android的@StringDef注释

这允许您设置允许的值供您检查并在代码中设置,同时允许将实际值设置为任何String。如果您从数据库中获得了错误的值,这将很有用。它与枚举非常相似。您还可以使用它为自己提供常量名称,该名称与您从Firebase接收到的可能的String值不同。

您可以CountryCode按以下方式更改枚举:

public class ClassYouAreConvertingTo {
    private static final String NETHERLANDS = "NLD";
    private static final String UNKNOWN = "???";

    @StringDef({NETHERLANDS, UNKNOWN})
    @Retention(RetentionPolicy.SOURCE)
    private @interface CountryCode {}

    private String countryCode;

    @CountryCode
    public String getCountryCode() {
        return this.countryCode;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在可以将国家/地区代码设置为任何值,但是Android Studio会尝试确保在检查字符串相等性时仅使用NETHERLANDS和UNKNOWN常量。使用其他值时,您会得到红色下划线(尽管该应用仍会编译并运行)。

IMO这是比枚举路由更安全,更好的解决方案。接受所有值,但只关心代码中的期望值。