My JSON data looks like this from server api
{
//...
PredecessorIds:[[1,2][3,4][5]]
//...
}
I can successfully handle the arrays of Integer or String by RealmList<RealmInt> but this time I failed with an error, because RealmList> is not supported saying, "Type parameter 'io.realm.realmList' is not within its bounds...."
For RealmInt see this link.
I tried to solve it using RealmList<RealmLista> where RealmLista extends from RealmObject and has a RealmList like this
public class RealmLista extends RealmObject {
public RealmList<RealmInt> value;
public RealmLista() {
}
public RealmLista(RealmList<RealmInt> val) {
this.value = val;
}
}
and then created a RealmListaTypeAdapter and added it to Gson but when deserializing Gson expects an Object (RealmLista) but is found array, as the data shown above from server is obvious.
//RealmListAdapter for Gson
#Override
public RealmLista read(JsonReader in) throws IOException {
RealmLista lista = new RealmLista();
Gson gson = new Gson();
//how to read that [[1],[3,4]] int into RealmLista
in.beginArray();
while (in.hasNext()) {
lista.value.add(new RealmInt(in.nextInt()));
}
in.endArray();
return lista;
}
Is there any way to store a simple List<List<Integer>> by converting to RealmObject of any type while saving, List<List<Integer>> is easily converted by Gson. :-/
Realm doesn't support lists of lists currently. See https://github.com/realm/realm-java/issues/2549.
So #EpicPandaForce's idea about creating a RealmObject that holds that inner list is probably the best work-around.
It could look something like this:
public class Top extends RealmObject {
private RealmList<ChildList> list;
}
public class ChildList extends RealmObject {
private RealmList<RealmInt> list;
}
public class RealmInt extends RealmObject {
private int i;
}
The correct link for the gist should be: https://gist.github.com/cmelchior/1a97377df0c49cd4fca9
Related
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.
currently I'm playing with GSON and got into some trouble that I couldn't solve on my own.
I've got these three classes:
One abstract class CustomEntity
public abstract class CustomEntity {
private View customView;
public CustomEntity() {}
public void setCustomView(View customView) {
this.customView = customView;
}
public View getCustomView() {
return customView;
}
}
Another class LastChange which extends from CustomEntity
public class LastChange extends CustomEntity {
public Config config;
public LastChange() {}
#Override
public String toString() {
return "LastChange:" + config.toString();
}
public Config getConfig() {
return config;
}
public void setConfig(Config config) {
this.config = config;
}
}
And a third class Config
public class Config extends CustomEntity {
public String config;
public String nav_items;
public Config() {}
#Override
public String toString() {
return "config data:" + config + ", " + nav_items;
}
public String getConfig() {
return config;
}
public void setConfig(String config) {
this.config = config;
}
public String getNav_items() {
return nav_items;
}
public void setNav_items(String nav_items) {
this.nav_items = nav_items;
}
}
In the MainActivity I've tried to deserialize the following JSON into a LastChange object with GSON.
String lastChangeJson = "{\"config\":{\"config\":\"2016-07-20 15:32:14\",\"nav_items\":\"2016-08-24 12:36:06\"},\"background_images\":{\"background_url_landscape\":\"2015-07-28 17:21:56\",\"background_url\":\"2015-07-28 17:21:56\",\"icon_for_accessory_view\":\"2015-07-28 17:21:56\",\"icon_for_route_view\":\"2015-07-28 17:21:56\",\"background_url_landscape_big\":\"2015-07-28 17:21:56\",\"background_url_big\":\"2015-07-28 17:21:56\"},\"nav_content\":[{\"last_change\":\"2016-06-29 11:06:16\",\"pageId\":\"10262\"},{\"last_change\":\"2016-08-24 12:36:06\",\"pageId\":\"10264\"},{\"last_change\":\"2016-08-09 16:13:03\",\"pageId\":\"10378\"},{\"last_change\":\"2016-08-09 16:13:03\",\"pageId\":\"10263\"},{\"last_change\":\"2016-07-20 15:32:14\",\"pageId\":\"10265\"}]}";
CustomEntity lastChangeEntity = gson.fromJson(lastChangeJson, LastChange.class);
The code above gives me the following exception:
java.lang.SecurityException: Can't make method constructor accessible
at java.lang.reflect.Constructor.setAccessible(Constructor.java:336)
at com.google.gson.internal.ConstructorConstructor.newDefaultConstructor(ConstructorConstructor.java:101)
at com.google.gson.internal.ConstructorConstructor.get(ConstructorConstructor.java:83)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:99)
at com.google.gson.Gson.getAdapter(Gson.java:423)...
But if I remove the attribute "customView" from the class CustomEntity and its getter and setter, the deserialization works fine.
Anybody got an idea on how I can tell GSON to ignore class attributes, if they don't appear in my json?
Thanks in advance.
When building new gson instance:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Annotate every field you want to serialize with #Expose:
#Expose
public Config config;
Edit:
One small side note - try to avoid keeping references to views (or any Context-related objects) in your models. You may encounter memory leaks if you persist instances of this model in static way and it smells like mixing presentation and data layers, which is never good thing to do for code readability and maintainability.
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.
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.
I use gson.fromJson(jsonStr, JVisitorResponse) method to parse the JSON reponse string, all were parsed except the inner class JUserUrls, there must be something wrong, my wrong usage of Gson caused this, please help me.
My outer reponse class defined below:
public class JVisitorResponse extends BaseResponsePojo {
public int count;
#SerializedName("visitor_list")
public ArrayList<JVisitor> visitorList;
public JVisitorResponse() {
}
}
JVIsitor class defined below:
public class JVisitor {
#SerializedName("user_id")
public long uid;
#SerializedName("user_name")
public String userName;
#SerializedName("user_urls")
public JUserUrls userHeadUrls;
}
JUserUrls class defiend below:
public class JUserUrls {
#SerializedName("main_url")
public String mainUrl;
}
my JSON response was
{
count:30,
visitor_list:[
{
user_id:333333,
user_name:"jason lee",
user_urls:{
main_url:"http://xxxxxxxxxx.jpg"
}
},
...
]
}