Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 483
Dennis van Opstal Avatar asked Mar 15 '18 14:03

Dennis van Opstal


People also ask

How do I convert a string to an Enum in Java?

You can create Enum from String by using Enum. valueOf() method. valueOf() is a static method that is added on every Enum class during compile-time and it's implicitly available to all Enum along with values(), name(), and cardinal() methods.

Can you convert string to Enum?

Use the Enum. IsDefined() method to check if a given string name or integer value is defined in a specified enumeration. Thus, the conversion of String to Enum can be implemented using the Enum. Parse ( ) and Enum.

What does firebase firestore () do?

Cloud Firestore caches data that your app is actively using, so the app can write, read, listen to, and query data even if the device is offline.

Can I use firestore with node js?

If you are developing a Web or Node. js application that accesses Cloud Firestore on behalf of end users, use the firebase Client SDK. Note: This Cloud Firestore Server SDK does not support Firestore databases created in Datastore mode. To access these databases, use the Datastore SDK.


1 Answers

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.

like image 99
Ben Kane Avatar answered Oct 09 '22 07:10

Ben Kane