Saving firebase ServerValue.TIMESTAMP on AutoValue POJOs - android

Android chat crashes on DataSnapshot.getValue() for timestamp
I'm trying to add a timestamp property to my POJO. The solution above tells jackson to ignore the real data member which is used by the application. I'm using AutoValue and can't figure out how I could annotate my class to get it to work.
#AutoValue
public abstract class Pojo {
#JsonProperty("id") public abstract String id();
#JsonProperty("name") public abstract String name();
#JsonProperty("date") public abstract long date();
#JsonCreator public static Pojo create(String id, String name, long date) {
return new AutoValue_Pojo(id, name, date);
}
}
I tried using a custom serializer:
public class TimeStampSerializer extends JsonSerializer<Long> {
#Override public void serialize(Long value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(ServerValue.TIMESTAMP.toString());
}
}
but that wrote the string date: "{.sv=timestamp}" to firebase instead of generating the timestamp

Caught my mistake:
#AutoValue
public abstract class Pojo {
#JsonProperty("id") public abstract String id();
#JsonProperty("name") public abstract String name();
//Custom serializer
#JsonSerialize(using = TimestampSerializer.class) #JsonProperty("date") public abstract long date();
#JsonCreator public static Pojo create(#JsonProperty("id") String id, #JsonProperty("name") String name, #JsonProperty("date") long date) {
return new AutoValue_Pojo(id, name, date);
}
}
public class TimestampSerializer extends JsonSerializer<Long> {
#Override public void serialize(Long value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
//Use writeObject() instead of writeString()
jgen.writeObject(ServerValue.TIMESTAMP);
}
}

Related

How can i serialize a Realm object which has a custom class

I'm having a problem with building Realm object which has a custom class.
#Parcel(
value = Parcel.Serialization.BEAN,
analyze = { Message.class })
#RealmClass
public class Message implements Comparable<Message>, RealmModel {`
#PrimaryKey
#Index
private long id;
#JsonProperty("thread_id")
long threadId;
#JsonProperty("message")
public String message;
#JsonProperty("user")
public User user;
...
}
When server send json response, try to parse as Message realm object with
realm.createObjectFromJson(MesssageMessage.class, JSONObject)
The problem is User. I've got an compile error "Filed user is not supported".
Below is the User class which is not realm object.
#JsonIgnoreProperties("incomplete_signed_up")
public class User implements KeepClassFromProguard, Parcelable {
public static final Parcelable.Creator<User> CREATOR = new
Parcelable.Creator<User>() {
#Override
public User createFromParcel(Parcel in) {
return new User(in);
}
#Override
public User[] newArray(int size) {
return new User[size];
}
};
public long id;
#JsonProperty("account_id")
private long accountId;
#JsonProperty("display_name")
public String display_name;
#JsonProperty("nick_name")
public String nickname;
#JsonProperty("user_detail")
public UserDetail userDetail;
...
}
I read https://gist.github.com/cmelchior/ddac8efd018123a1e53a and http://parceler.org/#getting_parceler, but I could't get answer yet.
I can't change every class to realm object because they all have another custom classes.
Does anyone know how this is handled? Hope there is any good example.
Thanks.
All references in a RealmModel must reference other RealmModel classes. This is the only way we can persist them.
You can #Ignore the user field, but then it will not be saved by Realm.

No setter/field for found Android Firebase

I used FirebaseRecyclerAdapter to get all the childs of "Pro" using a Model class named " Spacecraft" and now I want to retrieve all the candidates into a child of Pro like "1"
I created a public static "candidat" into "Spacecraft" and I used the setters and getters but still the same error
This is my database:
this is the Model Class
public class Spacecraft{
private String name;
private String desc;
private String last;
private candidat candidat;
public Spacecraft.candidat getCandidat() {
return candidat;
}
public void setCandidat(Spacecraft.candidat candidat) {
this.candidat = candidat;
}
public Spacecraft() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getLast() {
return last;
}
public void setLast(String last) {
this.last = last;
}
public static class candidat{
private String info;
private String namecandid;
public candidat(){}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public String getNamecandid() {
return namecandid;
}
public void setNamecandid(String namecandid) {
this.namecandid = namecandid;
}
}
}
This is my code for FirebaseRecyclerAdapter
#Override
protected void onStart() {
super.onStart();
FirebaseRecyclerAdapter<Spacecraft, candidatviewholder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Spacecraft, candidatviewholder>(
Spacecraft.class,
R.layout.candidat,
candidatviewholder.class,
query){
#Override
protected void populateViewHolder(candidatviewholder viewHolder, Spacecraft model, int position) {
viewHolder.setName1(model.getCandidat().getNamecandid());
viewHolder.setInfo1(model.getCandidat().getInfo());
}
};
rv.setAdapter(firebaseRecyclerAdapter);
}
The error:
No setter/field for key1 found on class com.example.ilyas.evotingapplication.Spacecraft$candidat
I had this error but the above solutions didn't fix it. Hopefully, this alternate solution will help others. If you have that error occur for almost every variable, chances are that you have Proguard enabled and it is removing the un-used getter and setter methods. To fix this, add a line similar to this to your proguard-rules.pro file:
-keep class com.example.yourapp.ObjectClass
where ObjectClass is the name of your java object class that is stored to Firebase.
I think it's just that your data models on Firebase and in Java differ.
In your java class, the Spacecraft class has a candidat field of type Candidat. But, in the database, the candidat field is really a nested object (map), containing one key Key1, which value is a Candidat structure.
So, depending on what did you want to achieve:
if you wanted each spacecraft to have exactly one candidat: save the database object properly, so {info: "info 1", namecandid: "name 1"} is saved directly under candidat field, not one level deeper, so the field has type Candidat in the code.
if you wanted each spacecraft to have a few candidats: instead of private Candidat candidat field, it should be typed Map<String, Candidat>, because that's the type it has in your database screenshot.
Work for me:
-keepclassmembers class com.myPackageName.MyClassName { *; }

Firebase with Realm. De-serializing POJO class

I have data in my firebase DB, everything works fine until I try to De-serialize the data.
Error: argument 1 has type io.realm.RealmList, got java.util.ArrayList
Here's my code:
DatabaseReference root = FirebaseDatabase.getInstance().
getReferenceFromUrl("https://swing-8792d.firebaseio.com/playlist");
Query playlistQuery = root.orderByKey().equalTo(key);
playlistQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
Log.d("Child", child + "");
Playlist receivedPlaylist = child.getValue(Playlist.class);
Playlist playlist = new Playlist();
playlist.setCreatedBy(receivedPlaylist.getCreatedBy());
playlist.setName(receivedPlaylist.getName());
playlist.setMyMap(receivedPlaylist.getMyMap());
playlist.setQrKey(receivedPlaylist.getQrKey());
playlist.setCount(receivedPlaylist.getCount());
playlist.setId(receivedPlaylist.getId());
playlist.setTracks(receivedPlaylist.getTracks());
mPlaylist.add(playlist);
}
This is my POJO class:
#RealmClass
public class Playlist extends RealmObject {
String name;
Long id;
RealmList<Track> tracks;
Integer count;
String createdBy;
RealmList<UserMap> myMap;
String qrKey;
public RealmList<UserMap> getMyMap() {
return myMap;
}
public void setMyMap(RealmList<UserMap> myMap) {
this.myMap = myMap;
}
public Playlist(){}
public String getQrKey() {
return qrKey;
}
public void setQrKey(String qrKey) {
this.qrKey = qrKey;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public RealmList<Track> getTracks() {
return tracks;
}
public void setTracks(RealmList<Track> tracks) {
this.tracks = tracks;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
}
If I try to de-serialize with Normal POJO class (i.e Removing Realm) it works fine.
Firebase won't work with classes that do not have default constructor or private variables i.e no public getter/setter.
A easier solution in your case would be to make a middleware class that is the same pojo just not extending RealmObject. Next initialise your RealmObject subclass using the values of the pojo.
Pseudo code
class SimplePojoPlaylist {
public String variable;
}
class Playlist extends RealmObject {
public String variable;
}
Then first cast into SimplePojoPlaylist
for (DataSnapshot child : dataSnapshot.getChildren()) {
SimplePojoPlaylist receivedPlaylist = child.getValue(SimplePojoPlaylist.class);
Playlist playList = new Playlist();
playList.variable = receivedPlaylist.variable;
}
RealmList is not a supported type for deserialization. Your database checks its structure and deduces that tracks should be an ArrayList. Then, when it tries to convert it, it finds that the types do not match.
Check this link from the docs:
Also, it is a good practice to make your objects immutable to avoid unwanted access and/or modifications.
Creating an empty object from scratch and then calling setter methods to define its state is not a very good pattern, because it can create a situation where an object is accessed before when its state is "broken".
If you need to create an object that is flexible, has a few mandatory fields and some optional, consider using the Builder pattern, although to do it you'd have to redesign your model.
wikipedia - Builder
If you don't need/want to use a builder, my advice is:
1) Make the empty constructor private and create another public one that requires all the fields.
2) Change your tracks field to be of type "List". Then, if you need the object to return a RealmList create another getter method such as tracksAsRealmList() that makes a RealmList out of the member list and returns it.
3) Make sure that the "Track" model has an empty private constructor, a public one with all of its parameters and that all of its fields are supported by firebase deserialization.
4) Unless strictly necessary, make your object fields private and set its value through a setter method.
I hope this helps you.

Android databinding with Firebase [duplicate]

I have data in my firebase DB, everything works fine until I try to De-serialize the data.
Error: argument 1 has type io.realm.RealmList, got java.util.ArrayList
Here's my code:
DatabaseReference root = FirebaseDatabase.getInstance().
getReferenceFromUrl("https://swing-8792d.firebaseio.com/playlist");
Query playlistQuery = root.orderByKey().equalTo(key);
playlistQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
Log.d("Child", child + "");
Playlist receivedPlaylist = child.getValue(Playlist.class);
Playlist playlist = new Playlist();
playlist.setCreatedBy(receivedPlaylist.getCreatedBy());
playlist.setName(receivedPlaylist.getName());
playlist.setMyMap(receivedPlaylist.getMyMap());
playlist.setQrKey(receivedPlaylist.getQrKey());
playlist.setCount(receivedPlaylist.getCount());
playlist.setId(receivedPlaylist.getId());
playlist.setTracks(receivedPlaylist.getTracks());
mPlaylist.add(playlist);
}
This is my POJO class:
#RealmClass
public class Playlist extends RealmObject {
String name;
Long id;
RealmList<Track> tracks;
Integer count;
String createdBy;
RealmList<UserMap> myMap;
String qrKey;
public RealmList<UserMap> getMyMap() {
return myMap;
}
public void setMyMap(RealmList<UserMap> myMap) {
this.myMap = myMap;
}
public Playlist(){}
public String getQrKey() {
return qrKey;
}
public void setQrKey(String qrKey) {
this.qrKey = qrKey;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public RealmList<Track> getTracks() {
return tracks;
}
public void setTracks(RealmList<Track> tracks) {
this.tracks = tracks;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
}
If I try to de-serialize with Normal POJO class (i.e Removing Realm) it works fine.
Firebase won't work with classes that do not have default constructor or private variables i.e no public getter/setter.
A easier solution in your case would be to make a middleware class that is the same pojo just not extending RealmObject. Next initialise your RealmObject subclass using the values of the pojo.
Pseudo code
class SimplePojoPlaylist {
public String variable;
}
class Playlist extends RealmObject {
public String variable;
}
Then first cast into SimplePojoPlaylist
for (DataSnapshot child : dataSnapshot.getChildren()) {
SimplePojoPlaylist receivedPlaylist = child.getValue(SimplePojoPlaylist.class);
Playlist playList = new Playlist();
playList.variable = receivedPlaylist.variable;
}
RealmList is not a supported type for deserialization. Your database checks its structure and deduces that tracks should be an ArrayList. Then, when it tries to convert it, it finds that the types do not match.
Check this link from the docs:
Also, it is a good practice to make your objects immutable to avoid unwanted access and/or modifications.
Creating an empty object from scratch and then calling setter methods to define its state is not a very good pattern, because it can create a situation where an object is accessed before when its state is "broken".
If you need to create an object that is flexible, has a few mandatory fields and some optional, consider using the Builder pattern, although to do it you'd have to redesign your model.
wikipedia - Builder
If you don't need/want to use a builder, my advice is:
1) Make the empty constructor private and create another public one that requires all the fields.
2) Change your tracks field to be of type "List". Then, if you need the object to return a RealmList create another getter method such as tracksAsRealmList() that makes a RealmList out of the member list and returns it.
3) Make sure that the "Track" model has an empty private constructor, a public one with all of its parameters and that all of its fields are supported by firebase deserialization.
4) Unless strictly necessary, make your object fields private and set its value through a setter method.
I hope this helps you.

Error in passing data between activites android while using a custom object

when I pass this object with intent, its not starting the activity.
Custom object code
public class UserVo implements Serializable {
String name,id;
String can_reply,can_view;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public String getCan_reply() {
return can_reply;
}
public void setCan_reply(String can_reply) {
this.can_reply = can_reply;
}
public String getCan_view() {
return can_view;
}
public void setCan_view(String can_view) {
this.can_view = can_view;
}
public void setId(String id) {
this.id = id;
}
Sending data through the intent
intent.putExtra("UserVo",vo);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
Receiving data
intent=getIntent();
UserVo vo= (UserVo) intent.getSerializableExtra("UserVo");
I also tried bundle, but it still does not work.
Look at this link Serializable:
Every serializable class is assigned a version identifier called a serialVersionUID. By default, this identifier is computed by hashing the class declaration and its members. This identifier is included in the serialized form so that version conflicts can be detected during deserialization. If the local serialVersionUID differs from the serialVersionUID in the serialized data, deserialization will fail with an InvalidClassException.
To avoid it:
You can avoid this failure by declaring an explicit serialVersionUID. Declaring an explicit serialVersionUID tells the serialization mechanism that the class is forward and backward compatible with all versions that share that serialVersionUID. Declaring a serialVersionUID looks like this: private static final long serialVersionUID = 0L;
and remember that:
If you declare a serialVersionUID, you should increment it each time your class changes incompatibly with the previous version. Typically this is when you add, change or remove a non-transient field.
For best practise, I recommend you to use Parcelable. It's harder for coding but better for performance.
If for some reason, you can't make your object a Parcelable, try adding a serialVersionUID. You can generate it through your IDE or choose one yourself.
For UserVo you will have
UserVo implements Parcelable {
...
...
public UserVo (){
}
#Override public int describeContents() {
return 0;
}
#Override public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
+ other data ...writInt etc
}
public void readFromParcel(Parcel source){
name = source.readString();
+ other data ..keep the same order
}
public static final Parcelable.Creator<DocumentFile> CREATOR = new Parcelable.Creator<UserVo>() {
#Override
public UserVo createFromParcel(Parcel source) {
return new UserVo(source);
}
#Override
public UserVo[] newArray(int size) {
return new UserVo[size];
}
};
public UserVo(Parcel source )
{
readFromParcel(source);
}
Also have a look at annotations library , it will be easly to send data using #Extra annotation .
In order to send the custom objects from one Activity to Other, you have to serialize the objects with serialVersionID. Try the below modified code for your requirement.
UserVo.java
public class UserVo implements Serializable {
private static final long serialVersionUID = 1L;
String name,id;
String can_reply,can_view;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public String getCan_reply() {
return can_reply;
}
public void setCan_reply(String can_reply) {
this.can_reply = can_reply;
}
public String getCan_view() {
return can_view;
}
public void setCan_view(String can_view) {
this.can_view = can_view;
}
public void setId(String id) {
this.id = id;
}
ActivityA.java - To send the object through Intent
Intent objectIntent = new Intent(ActivityA.this, ActivityB.class);
i.putExtra("sendObjects", UserVo ); //Your object here
startActivity(objectIntent);
ActivityB.java - To Receive the Intent
UserVo mReceiveObjects = (UserVo)getIntent().getExtras().get("sendObjects");

Categories

Resources