I'm using rushorm for sqlite object serializing and storage. It work pretty cool so far. The problem is that I wan't to store following Request object:
public class Request extends RushObject {
private String url;
private RequestParamsStorable params;
public Request() {}
public Request(String aUrl, RequestParamsStorable aParams)
{
this.url = aUrl;
this.params = aParams;
}
public String getUrl()
{
return this.url;
}
public RequestParams getParams()
{
return this.params;
}
}
As you can see I need to store RequestParams object. In order to store it, as I obviously cannot make it to extend RushObject I made a subclass and made it to implement Rush as per docs instructions:
public class RequestParamsStorable extends RequestParams implements Rush {
public RequestParamsStorable() {}
#Override
public void save() { RushCore.getInstance().save(this); }
#Override
public void save(RushCallback callback) { RushCore.getInstance().save(this, callback); }
#Override
public void delete() { RushCore.getInstance().delete(this); }
#Override
public void delete(RushCallback callback) { RushCore.getInstance().delete(this, callback); }
#Override
public String getId() { return RushCore.getInstance().getId(this); }
}
It didn't throw any errors and calling save() on Request object went smoothly. When I ask for stored objects like that:
List<Request> remainingsInDB = new RushSearch().find(Request.class);
I indeed receive stored Request objects, with proper url, however RequestParamsStorable is empty(""). I checked and when I save them, they definetely had values, and are not empty.
So the question is where I'm wrong?
Regards,
If your parent class RequestParams contains fields declared as final, they will not be restored.
As reference you can see the RushORM source code
ReflectionClassLoader.java
for (Field field : fields) {
field.setAccessible(true);
if (!annotationCache.get(clazz).getFieldToIgnore().contains(field.getName())) {
if (!loadJoinField(object, rushColumns, annotationCache, field, joins, joinTables)) {
if(rushColumns.supportsField(field)) {
String value = values.get(counter);
if(value != null && !value.equals("null")) {
rushColumns.setField(object, field, value);
}
counter++;
}
}
}
}
You should either remove final modifier of the fields or create wrapper object which will be stored in the db and then restore RequestParams from it
Okay the problem indeed is fields declared as final in RequestParams. #Stuart Campbell properly noted RequestParams reference. Now what I'm trying as a workaround is to store all properties(except Wrappers) as JSONObject, store this JSONObject as String and then restore state from it. I'm facing some issues with JsonWritter and didn't solved my issue yet. Not sure if it is a good idea to post relevant code here, or to post new question though?
Related
I push data to Firebase using Order object, the question is I want the first letter of every child name capital. I defined the property like "Complain" but in Firebase it still shows as "complain", I dont know how to make it.
The current structure of the Firebase:
The structure I want:
I defined the property like this:
#Data
public class Order implements Serializable {
#SerializedName("Complain")
private String Complain;
public Order() {
Complain = "";
}
public String getComplain() {
return Complain;
}
public void setComplain(String complain) {
Complain = complain;
}
}
I push data to Firebase like this:
Map<String, Object> map = new HashMap<>();
map.put(orderSavePath, order);
reference.updateChildren(map).addOnCompleteListener(listener);
The Firebase JSON serialization name is controlled by the annotation PropertyName.
public class Order implements Serializable {
private String Complain;
public Order() {
Complain = "";
}
#PropertyName("Complain")
public String getComplain() {
return Complain;
}
#PropertyName("Complain")
public void setComplain(String complain) {
Complain = complain;
}
}
The annotation needs to be on both the getter and the setter. Alternatively you can just use public fields and reduce the class to:
public class Order {
#PropertyName("Complain")
public String Complain;
}
I have a model class to store Firebase User information. Inside of the model class I have a HashMap to store all of the data inside. Once I have stored the data, the I push the Hashmap into the Firebase database. The values store fine, but I cannot access the values. Every time I try to access them, I get an error saying that I am attempting to invoke a virtual method on a null object reference.
mDatabase.child("users").child(mUserId).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot ChildSnapshot, String s) {
// These two lines of code give the error.
User author = ChildSnapshot.child("profile").getValue(User.class);
String author_username = author.getUsername();
These give me the error. I am attempting to grab data from the child of the snapshot. Why is this giving me an error? Is there a better way to do this?
JSON Firebase snapshot:
Model class:
//TODO model class for the user. This way I can set the values for each user. I will be adding more values in the future.
public class User {
public HashMap<String, String> hashMap = new HashMap<String,String>();
public User() {
}
public User(String username) {
hashMap.put("username",username);
}
public String getUsername(){
return hashMap.get("username");
}
}
In case somebody else was struggling with this issue, I wanted to give an answer. Inside of my ChildEventListener, the profile is the key in this situation so when I use ChildSnapshot.child("profile").getValue(User.class) it returns a null value. Also, (I'm not quite sure why this is) the value of the username was stored in a different class called User_message which was used to store the message. so my updated code looks something like this:
mDatabase.child("users").child(mUserId).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot ChildSnapshot, String s) {
User_message author = ChildSnapshot.getValue(User_message.class);
String author_username = author.getUsername();
I was facing the same problem and spent more than 5 hours. I added Default Constructor of the model and this solves my problem.
public class User {
public String email;
public String name;
public User() {
}
public User(String email, String name) {
this.email = email;
this.name = name;
}}
I hope this will help you. Thanks
I got a response back from the API like this below:
{
"transactions": [null]
}
However, when I tried to debug, List.getTransactionItems().size() is equal 1 rather than 0. I think it considers null as an item. Also, I checked few things as below but none of them work.
if (this.transactionsViewModel.getTransactionItems().size() == 0
|| this.transactionsViewModel.getTransactionItems() == null
|| this.transactionsViewModel.getTransactionItems().isEmpty()
|| this.transactionsViewModel.getTransactionItems().equals(null))
However, when I tried to call something like that below, it actually recognized that there is an null item in the list.
this.transactionsViewModel.getTransactionItems().contains(null)
Any idea in this situation?
Thanks in advance.
As far as i know, json array not supposed to be null like that. You can make "transactions": [] or "transactions": null instead. That behaviour happens because your code recognise that the array is not empty. It has 1 item with null value.
If you can not change the server response.
You can check if the list only contains null by removeAll null object from your list then check the size
(this.transactionsViewModel.getTransactionItems().removeAll(Collections.singleton(null))).size() == 0
// size == 0 your list only contains null
// size > 0 your list is not empty
How about reading the stream into a string, and doing a search and replace on the json string, and remove the null, before you parse the json? You can also extend a stream class, and do it in the stream.
This is the problem about the unsuitable response from the server. To solve your problem, you can only to make some workaround in your code.
I suggest you should keep your application as clear and origin as possible. Don't change the condition call if it is really necessary.
The following is my suggested code ( used some code from Phan Van Linh)
This class used Retrofit library as the example to make the callback abstract
public class TestFragment extends BaseFragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
view = inflater.inflate(R.layout.testlayout, null, false);
RestA rest = UtilRetrofit.build(RestA.class);//from the restful endpoint
Call<YourObject> call = rest.getTransactions();
call.enqueue(new TransactionCallback() {
#Override
public void onResponse(YourObject bean) {
Toast.makeText(getActivity(), "Hello World", Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<YourObject> call, Throwable t) {
}
});
return view;
}
public abstract class TransactionCallback implements Callback<YourObject> {
#Override
public void onResponse(Call<YourObject> call, Response<YourObject> response) {
YourObject lNewBean = response.body();
lNewBean.getTransactions().removeAll(Collections.singleton(null));// do some custom action and remove useless thing
onResponse(lNewBean);
}
public abstract void onResponse(YourObject bean);
}
/**
* Created by me on 22/8/2016.
*/
public static class YourObject {
private List<String> transactions;
public List<String> getTransactions() {
return transactions;
}
public void setTransactions(List<String> pTransactions) {
transactions = pTransactions;
}
}
}
I want to return an object with some things in them.
Here is the declaration;
Object user_det = get_user_det();
Here is the function code:
private Object get_user_det() {
Firebase f_user = new Firebase("https://myapp.firebaseio.com/User/");
f_user.addListenerForSingleValueEvent(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot snap_user) {
// TODO Auto-generated method stub
Iterable<DataSnapshot> rs = snap_user.getChildren();
Iterator<DataSnapshot> irs = rs.iterator();
long allNum2 = snap_user.getChildrenCount();
int maxNum2 = (int)allNum2;
int count_user = 1;
while(irs.hasNext())
{
if(count_user <= maxNum2)
{
Firebase user_data = new Firebase("https://myapp.firebaseio.com/");
AuthData authData = user_data.getAuth();
Map<String, Object> nPost = (Map<String, Object>) irs.next().getValue();
String db_email = nPost.get("email_addr").toString();
if (authData != null) {
String usr_email = authData.getProviderData().get("email").toString();
if(usr_email.equals(db_email))
{
//NB: I WANT TO ADD THE FOLLOWING INTO THE OBJECT
String disp_name = nPost.get("disp_name").toString();
String real_name = nPost.get("real_name").toString();
}
} else {
System.out.println("Failed");
}
}
count_user++;
}
}
#Override
public void onCancelled(FirebaseError arg0) {
// TODO Auto-generated method stub
}
});
return null; //NB: I NEED TO RETURN THE OBJECT HERE.
}
I want to return the string disp_name and real_name but they are inside the addListenerForSingleValueEvent, so how do I get them out and return it to the function.
I have wrote "NB" in the code where I need help with.
Thanks for your time.
If you want to return an object from your method in java, do it like this:
The Object class:
This contains the structure of your Object, and defines what data will be in it. Also includes methods to easily get the data.
private class myObject {
private String name;
private String realName;
//The constructor, so you can set the data when creating the Object.
public myObject (String disp_name, String real_name) {
name = disp_name;
realName = real_name;
}
//Getter methods, to get the data.
public String getRealName() {return realName;}
public String getDisplayName() {return name;}
}
Your code:
private Object get_user_det() {
myObject o; //Declare it, so it can be returned.
...
String disp_name = nPost.get("disp_name").toString();
String real_name = nPost.get("real_name").toString();
o = new myObject(disp_name, real_name); //create it and set the data.
...
return myobject; //return the new Object with the data.
}
To get the data from the Object:
myObject o = get_user_det(); //Call the metod which return our Object.
String realName = o.getRealName(); //Get the data from the Object.
String displayName = o.getDisplayName;
In your case, it would be much easier to use a String array.
Hope this helps.
It's probably easiest to see what's going on, if you add some printlns to your code:
private Object get_user_det() {
Firebase f_user = new Firebase("https://myapp.firebaseio.com/User/");
System.out.println("Adding listener");
f_user.addListenerForSingleValueEvent(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot snap_user) {
System.out.println("Data received");
}
#Override
public void onCancelled(FirebaseError arg0) {
// TODO Auto-generated method stub
}
});
System.out.println("Returning");
return null; //NB: I NEED TO RETURN THE OBJECT HERE.
}
If you execute this code, you will see that it logs:
Adding listener
Returning
Data received
Most likely, this is not what you expected. But hopefully, it makes sense if you read my explanation below.
Asynchronous loading
When you register your listener:
f_user.addListenerForSingleValueEvent(new ValueEventListener(){
You tell Firebase to start listening for events. It goes off and starts retrieving the data from the server.
Since retrieving the data may take some time, it does this retrieval asynchronously so that your thread isn't blocked. Once the data is completely retrieved, Firebase calls the onDataChange method in your listener.
Between the time you start listening and the time onDataChange is called, your code continues executing. So there is no way to return data that is loaded asynchronously, because by the time your function returns, the data isn't loaded yet.
Solutions
Disclaimer: I am not an expert at solving this problem in Java, so there may be problems with my solutions. If^H^HWhen you find any, please report them in the comments.
I know of three possible solutions to the problem:
force the code to wait for the data to be returned
return a Future that at some point will contain the data
pass a callback into get_user_det and call that function once the data is available
You will probably be tempted to selected option 1, since it matches most closely with your mental modal of loading data. While this is not necessarily wrong, keep in mind that there is a good reason that the loading is done asynchronously. It might be worth taking the "learning how to deal with asynchronicity" penalty now.
Instead of writing up examples for all solutions, I'll instead refer to some relevant questions:
Retrieving data from firebase returning NULL (an answer that uses approach 3)
Is waiting for return, ok?
Java wait() & notify() vs Android wait() & notify() (a question from a user taking approach 1)
How it works:
Firebase uses reflection to build a JSON tree object to save to the database. When you retrieve this JSON tree, you can cast it back to your original object. Just like serializing and deserializing. This means you do not need to handle the keys and values when trying to "rebuild" your object like you are. It can all be done like so:
YourObject object = (YourObject) dataSnapshot.getValue(YourObject.class);
Notice the YourObject.class in the getValue(). This tells firebase to reflect through this class and find the appropriate accessors with the dataSnapshot.
How to do it
Be sure that your object has:
Accessors Appropriate getters and setters for ALL fields - (or annotated with #JsonIgnore if you wish to not save a particular field)
Empty constructor. Your object must provide a constructor that does not modify itself at all.
What your object should look like:
public class YourObject {
private String displayName;
private String realName;
public YourObject() { /*Empty constructor needed for Firebase */ }
// Accessors
public void setRealName(String realName){
this.realName = realName;
}
public String getRealName(){
return this.realName;
}
public String getDisplayName(){
return this.displayName;
}
public void setDisplayName(String displayName){
this.displayName = displayName;
}
}
Then, in any of the firebase callbacks, you can just cast your DataSnapshot in to your object:
public void onDataChange(DataSnapshot snap_user) {
YourObject object = new Object;
if(snap_user.getValue() != null) {
try {
object = (YourObject) snap_user.getValue(YourObject.class); <-- Improtant!!!
} catch(ClassCastException ex) {
ex.printStackTrace();
}
}
return object;
}
Also
It seems you are retrieving many objects. When doing this, I find it best to use the onChildEventListener then for each of the YourObjects in that node, onChildAdded(DataSnapshot ds, String previousChild); will be called.
I finally managed to get an object out of an AsyncTask (DogAsyncTask) with an interface/listener (DogListener):
public String fout(String url) {
Dog d_in = new Dog("DogName");
DogAsyncTask task = new DogAsyncTask(d_in);
final String out = ""; <---Have tried but error for out becomes "The final local variable out cannot be assigned, since it is defined in an enclosing type"
task.setDogListener(new DogListener()
{
#SuppressWarnings("unchecked")
#Override
public void DogSuccessfully(String data) { <---The string I want to get
Log.e("DogListened", data);
out = data;
}
#Override
public void DogFailed() {}
});
task.execute(url);
return out;
}
My main activity calls this function (fout) and is supposed to get a String out of it. String data is already there and Log.e("DogListened", data); records it too. How can I return that outwards to my main activity? I have tried setting out = data and made out a final String on the outside but the "final local variable out cannot be assigned, since it is defined in an enclosing type" error comes up. How can I get around this?
Thanks
I guess you cannot access to out because it is out of the listener's scope. You can maybe pass your out as a reference parameter to the constructor of your DogListener.
final String out = "";
task.setDogListener(new DogListener( **out** )
{
#SuppressWarnings("unchecked")
#Override
public void DogSuccessfully(String data) {
Log.e("DogListened", data);
out = data;
}
#Override
public void DogFailed() {}
});
BUT honestly I donT know how to pass parameters as a reference in Java like in C#..
EDIT:
This can help you too: Can I pass parameters by reference in Java?
Use a value holder, which basically is an object which stores some value in a field. The value holder can be assigned as final, but you can change the value. Follow the link for more info on this pattern: http://martinfowler.com/eaaCatalog/lazyLoad.html
However you can use your string as value holder: Just append to the empty string assigned to out. Or better use a StringBuffer object.