8

I'm using Firebase for data storage on an Android project, and using the Firebase Java API to deal with data. I'm not sure I'm doing it as efficiently as possible, though, and I'd like some advice on best practices for retrieving and formatting data. My Firebase repository looks something like this....

-POLLS
    NUMPOLLS - 5
    (pollskey) - NAME - Poll1
            NUMELECTIONS - 2
            ELECTIONS 
                (electionskey) - NAME - Election1
                        NUMNOMINATIONS - 2
                        NUMVOTERS - 2
                        NUMBERTOELECT - 1
                        VOTERS - (votesrkey) - NAME - Charles
                                         NUMBER - (678) 333-4444
                                 .
                                 .
                                 .
                                 (voterskey) - ...
                        NOMINATIONS - (nominationskey) - NAME - Richard Nixon
                                              NUMBEROFVOTES - 2
                                       .
                                       .
                                       .
                                      (nominationskey) - ...
            .
            .
            .
            (electionskey) - ...
     .
     .
     .
     (pollskey) - ...

So, for example here I'm trying to get all data out of a poll to list poll name, it's election names, and the candidate names and number of votes for each election. I get the POLLS level DataSnapshot during the OnCreate() function of my main activity like this...

private static final Firebase polls = pollsFirebase.child("Polls");
protected void onCreate(Bundle savedInstanceState) {

        polls.addListenerForSingleValueEvent(new ValueEventListener() {

            @Override
            public void onDataChange(DataSnapshot snapshot) {
                for (DataSnapshot child : snapshot.getChildren()) {
                    if (!child.getName().equals("NumPolls")) {
                        createPollTableAndHeaders(child);
                    }
                }

            }
        });
    }

Then I proceed to read out the individual pieces of data I need by successively calling getValue() on DataSnapshots, and checking the keys of the resulting HashMaps...

private void createPollTableAndHeaders(DataSnapshot poll) {
    String pollName = "";
    int numPolls;
    Object p = poll.getValue();
    if (p instanceof HashMap) {
        HashMap pollHash = (HashMap) p;
        if (pollHash.containsKey("Name")) {
            pollName = (String) pollHash.get("Name");
        } 
        if (pollHash.containsKey("Elections")) {
            HashMap election = (HashMap) pollHash.get("Elections");
            Iterator electionIterator = election.values().iterator();
            while (electionIterator.hasNext()) {
                Object electionObj = electionIterator.next();
                if (electionObj instanceof HashMap) {
                    HashMap electionHash = (HashMap) electionObj;
                    if (electionHash.containsKey("Name")) {
                        String electionName = (String) electionHash.get("Name");
                    }
                }
            };
        }
    }

This seems like a pretty tedious way to drill down through the data structure, and I'm wondering if there's a better way.

I've seen the getValue(java.lang.Class<T> valueType) method in the documentation, but haven't been able to get it to work in my case, since I'm working with composed objects and not just containers for primitive types. How does the function know what Firebase data to assign to which member variables of a model object? Does it match Firebase key names with member variables, and therefore do these have to be exactly the same, with case sensitivity? How would that deal with Firebase generated key names like produced when pushing to a List? How to you construct model objects for composed objects?

2 Answers2

17

The getValue(java.lang.Class valueType) method follows the same rules as the jackson object mapping library (it's what we use internally: http://wiki.fasterxml.com/JacksonInFiveMinutes). So, your Java classes must have default constructors (no arguments) and getters for the properties that you want assigned (https://www.firebase.com/docs/java-api/javadoc/com/firebase/client/DataSnapshot.html#getValue(java.lang.Class)). In short, yes, the key names in Firebase must match the member variables.

For an example using composite objects, including a list, see AndroidDrawing. Specifically, the Segment class contains a list of Point instances. There is one catch using lists of data generated with the .push() method. Since the key names generated are Strings so that they can be unique across clients, they deserialize the Maps rather than Lists. However, if you iterate over dataSnapshot.getChildren() they will be returned in order.

In addition, if you don't want to deserialize into a HashMap, you can use the child() method on DataSnapshot. For instance:


String pollName = poll.child("Name").getValue(String.class); 
DataSnapshot elections = poll.child("Elections");
for (DataSnapshot election : elections.getChildren()) {
    String electionName = election.child("Name").getValue(String.class);
}

In this example, any values that don't exist will be returned as null.

Hope that helps!

Greg Soltis
  • 1,434
  • 9
  • 10
  • Cool thanks Greg. Give me a couple hours to get home and try to implement this way. Thanks! –  Jul 29 '13 at 20:44
  • Ok cool, this is everything I need to get started. Thanks Greg, appreciate the quick support. –  Jul 30 '13 at 00:42
  • Sorry to bring up an old thread, but, I'm using GeoFire along with Firebase and the Geolocation class does not provide a default constructor. So, how should I proceed to deserialize this kind of class ? Is there any deserializer factory embeded? Thanks ! – Leonardo Dec 12 '16 at 20:08
1

public T getValue(Class valueType)

1.The class must have a default constructor that takes no arguments

2.The class must define public getters for the properties to be assigned. Properties without a public getter will be set to their default value when an instance is deserialized

Check it from: this source It'll help you

detail

Hoa Nguyen
  • 705
  • 8
  • 15