简体   繁体   中英

Firebase Cloud Firestore – Convert String to Java Enum

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') 

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

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

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.

The enum cases need to match the possible String values exactly, including capitalization.

For example, if your country can have values of "NLD" and "US" , your enum should be structured like this:

public enum CountryCode {
    NLD,
    US
}

This lets Firebase automatically convert the String to an enum for the model you're converting to.

Note: Firebase uses <YourEnumType>.valueOf("value-from-doc") to attempt to serialize to enums. You can't override valueOf for enums in Java, so this is the best we can do at this time for serializing Strings to enums.


That being said, if you do it that way, you're opening yourself to an exception if you receive a value that doesn't match any of your enum values. Instead, you can use Android's @StringDef annotation .

This allows you to set allowable values for you to check against and set in code, while allowing the actual value to be set to any String. This is useful if you get a bad value from your database. It's very similar to an enum. You can also use this to give yourself constant names that are different than possible String values you receive from Firebase.

You'd change your CountryCode enum as follows:

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

Now country code can be set to any value, but Android Studio tries to make sure you only use NETHERLANDS and UNKNOWN constants when checking String equality. You'll get a red underline when using a different value (although the app will still compile and run).

IMO this is a safer, better solution than the enum route. Accept all values, but only care about the expected values in code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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