26

When you're making a Plain Old Java Object that's meant to be serialized from and deserialized to Firebase, is there a way to use the ServerValue.TIMESTAMP value?

For example, let's say I want to have an object where one of the properties is the last time it was edited and you'd like to use the ServerValue.TIMESTAMP value.

In the POJO class, you might have:

private String timeLastChanged;

or

private Map<String, String> timeLastChanged;

In the first example with the String, I run into the issue of setting timeLastChange = ServerValue.TIMESTAMP;, because ServerValue.TIMESTAMP is a Map.

In the second example with the Map<String, String> I get a "failed to debounce" error because it can't properly deserialize the long stored in the database into a Map<String, String>. Is there any work around for this?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Lyla
  • 2,767
  • 1
  • 21
  • 23

6 Answers6

31

Update 12/27/2016

Switched out @JsonIgnore for @Exclude as many have mentioned.


I finally came up with a flexible solution for working with Dates and ServerValue.TIMESTAMP. This is working off of examples from Ivan V, Ossama, and puf.

I couldn't figure out a way to deal with the conversion between long and HashMap<String, String>, but if you nest the property in a more generic HashMap<String, Object> it can go into the database as either a single long value ("date", "1443765561874") or as the ServerValue.TIMESTAMP hash map ("date", {".sv", "servertime"}). Then when you pull it out, it will always be a HashMap with ("date", "some long number"). You can then create a helper method in your POJO class using the @JsonIgnore @Exclude annotation (meaning Firebase will ignore it and not treat it as a method for serializing to/from the database) to easily get the long value from the returned HashMap to use in your app.

Full example of a POJO class is below:

import com.google.firebase.database.Exclude;
import com.firebase.client.ServerValue;

import java.util.HashMap;
import java.util.Map;

public class ExampleObject {
    private String name;
    private String owner;
    private HashMap<String, Object> dateCreated;
    private HashMap<String, Object> dateLastChanged;

    /**
     * Required public constructor
     */
    public ExampleObject() {
    }

    public ExampleObject(String name, String owner, HashMap<String,Object> dateCreated) {
        this.name = name;
        this.owner = owner;
        this.dateCreated = dateCreated;

        //Date last changed will always be set to ServerValue.TIMESTAMP
        HashMap<String, Object> dateLastChangedObj = new HashMap<String, Object>();
        dateLastChangedObj.put("date", ServerValue.TIMESTAMP);
        this.dateLastChanged = dateLastChangedObj;
    }

    public String getName() {
        return name;
    }

    public String getOwner() {
        return owner;
    }

    public HashMap<String, Object> getDateLastChanged() {
        return dateLastChanged;
    }

    public HashMap<String, Object> getDateCreated() {
      //If there is a dateCreated object already, then return that
        if (dateCreated != null) {
            return dateCreated;
        }
        //Otherwise make a new object set to ServerValue.TIMESTAMP
        HashMap<String, Object> dateCreatedObj = new HashMap<String, Object>();
        dateCreatedObj.put("date", ServerValue.TIMESTAMP);
        return dateCreatedObj;
    }

// Use the method described in https://stackoverflow.com/questions/25500138/android-chat-crashes-on-datasnapshot-getvalue-for-timestamp/25512747#25512747
// to get the long values from the date object.
    @Exclude
    public long getDateLastChangedLong() {

        return (long)dateLastChanged.get("date");
    }

    @Exclude
    public long getDateCreatedLong() {
        return (long)dateCreated.get("date");
    }

}
Community
  • 1
  • 1
Lyla
  • 2,767
  • 1
  • 21
  • 23
  • 2
    With this example like the one below when I call `getDateLastChangedLong()` I get a NullPointerException probably because of casting to Long..the timestamp gets created properly though...any thoughts or workarounds for this? I basically copied and pasted your code before you ask for my code :) – Isaac Martin Otim Sep 04 '16 at 14:42
  • if I can have a c# version of this it will be awesome. thanks – A. Adam Feb 23 '17 at 04:26
  • Is anybody else facing CASTING issues? I'm getting java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Long – Pranav Mahajan Jul 26 '18 at 00:34
20

I wanted to improve Lyla's answer a little bit. First, I would like to get rid of public methods public HashMap<String, Object> getDateLastChanged() public HashMap<String, Object> getDateCreated(). In order to do that you can mark dateCreated property with @JsonProperty annotation. Another possible way to do so is to change property detection like that: @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
Second, I don't understand why we need to put ServerValue.TIMESTAMP into HashMap while we can just store them as property. So my final code is:

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.firebase.client.ServerValue;

public class ShoppingList {
    private String listName;
    private String owner;
    @JsonProperty
    private Object created;

    public ShoppingList() {
    }

    public ShoppingList(String listName, String owner) {
        this.listName = listName;
        this.owner = owner;
        this.created = ServerValue.TIMESTAMP;
    }

    public String getListName() {
        return listName;
    }

    public String getOwner() {
        return owner;
    }

    @JsonIgnore
    public Long getCreatedTimestamp() {
        if (created instanceof Long) {
            return (Long) created;
        }
        else {
            return null;
        }
    }

    @Override
    public String toString() {
        return listName + " by " + owner;
    }

}
sergey.n
  • 1,672
  • 19
  • 18
9

Those solution seems a bit difficult for me as I don't know what @JsonIgnore is doing. I tried to do it in a easy way and seems work.

private Object dateLastChanged;

public Object getDateLastChanged() {
    return dateLastChanged;
}

To get something human readable, I just past the return value dateLastChanged Object into the following method by casting it into Long.

static String convertTime(Long unixtime) {
    Date dateObject = new Date(unixtime);
    SimpleDateFormat dateFormatter = new SimpleDateFormat("dd-MM-yy hh:mmaa");
    return dateFormatter.format(dateObject);
}

Welcome to opinions on my solution^^

Daniel Chan
  • 343
  • 5
  • 13
  • Your getter method helped me when @snayde getter wasn't working for me (permission error) – josue.0 Sep 10 '16 at 09:58
  • They have to use jason ignore because the object is map and they store it as a simpler field – sivi Mar 06 '17 at 09:43
6

Instead of using @JsonIgnore, you could use the Firebase native @Exclude. For example, I work in a similar project and my model is like this.

package com.limte.app.borrador_1.mModel;

import com.google.firebase.database.Exclude;
import com.google.firebase.database.ServerValue;

/**
 * Created by Familia on 20/12/2016.
 */

public class ChatItem {
    String chatName;
    Long creationDate;


    public ChatItem() {
    }

    public String getChatName() {
        return chatName;
    }

    public void setChatName(String chatName) {
        this.chatName = chatName;
    }

    public java.util.Map<String, String> getCreationDate() {
        return ServerValue.TIMESTAMP;
    }

    @Exclude
    public Long getCreationDateLong() {
        return creationDate;
    }
    public void setCreationDate(Long creationDate) {
        this.creationDate = creationDate;
    }

}

And this code works! In Firebase, the results are: Results

1

The most common use for Server.TIMESTAMP is:

  1. set the server value when the data is SENT into the server
  2. fetch this model from firebase,and cast it easily to Long

This trick saved me the hard work of handle 2 different fields for only 1 value

    public class HandleTimestampFirebaseObject {

    Object timestamp;

    public HandleTimestampFirebaseObject() {

    }

    public Object getTimestamp(){
        if(timestamp instanceof Double){

      /*    this will handle the case of AFTER fetch from backend,and  
            serialize to object into SharedPreferences for example ,when the 
            Object casting automatically into Double.
            (Anyway,call (long)getTimestamp from your code who use this model*/
            return ((Double) timestamp).longValue();
        }
        else{
            return timestamp;  //this handle to firebase requirement in the server side(Object needed)
        }
    }
yarin
  • 543
  • 1
  • 5
  • 18
0

Same solution, but I use this.

@IgnoreExtraProperties
public class FIRPost {
    Object timestamp;

    public FIRPost() {
        // Default constructor required for calls to DataSnapshot.getValue(FIRPost.class)
        this.timestamp = ServerValue.TIMESTAMP;
    }

    public Object getTimestamp() {
        return timestamp;
    }

    @Exclude
    public Long getTimestamp(boolean isLong) {
        if (timestamp instanceof Long) return (Long) timestamp;
        else return null;
    }
}
wonsuc
  • 3,498
  • 1
  • 27
  • 30