I discovered when saving a POJO with a map field using Firebase on Android, that if that map contains nulls in the value property of that map, then the whole field is ignored.
The workaround is easy (non-null values will result in the map saving successfully), but I want to understand why is this so?
Model
public class Game {
private String owner;
private Map<String, String> characters;
public Game() {
// empty constructor for Firebase
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public Map<String, String> getCharacters() {
return characters;
}
public void setCharacters(Map<String, String> characters) {
this.characters = characters;
}
}
Calling code
final Game game = new Game();
game.setOwner("owner");
game.setCharacters(new HashMap<String, String>());
game.getCharacters().put("Bugs Bunny", null);
game.getCharacters().put("Batman", null);
final Firebase ref = new Firebase("<firebaseurl>/test/" + System.currentTimeMillis());
ref.setValue(game);
Resulting object in Firebase
{
"1456421340781": {
"owner": "owner"
}
}
They're actually not ignored. When you give Firebase a null value for a property/path, you indicate that you want to property or path to be deleted.
From the documentation on Firebase's JavaScript set() method:
Passing null for the new value is equivalent to calling remove(); all data at this location or any child location will be deleted.
So if you set a value with:
ref.child("keith").setValue(47649);
Then the following will delete it:
ref.child("keith").setValue(null);
This behavior is most useful when you use updateChildren(), but it works equally when you call setValue().
Related
I used an ArrayList to store data on Firebase.It got stored as a HashMap with key value starting from 0 and its value stored as string.
The database is as follows:
I am using POJO, to store and retrieve data.
My POJO is defined as follow:
#JsonIgnoreProperties(ignoreUnknown = true)
public class POJO_MemoriesRVDisplay {
String mem_name;
String mem_place_street_address;
HashMap<Object,Object> images;
public POJO_MemoriesRVDisplay() {
}
public HashMap<Object, Object> getImages() {
return images;
}
public void setImages(HashMap<Object, Object> images) {
this.images = images;
}
public POJO_MemoriesRVDisplay(String mem_name, String mem_place_street_address, HashMap<Object, Object> images) {
this.mem_name = mem_name;
this.mem_place_street_address = mem_place_street_address;
this.images = images;
}
public String getMem_name() {
return mem_name;
}
public void setMem_name(String mem_name) {
this.mem_name = mem_name;
}
public String getMem_place_street_address() {
return mem_place_street_address;
}
public void setMem_place_street_address(String mem_place_street_address) {
this.mem_place_street_address = mem_place_street_address;
}
}
When I run this code, I get an error such as:
Failed to bounce to type
How do I declare the POJO properly. I have tried several posts but couldn't help it. Tried changing the declaration of images to strings, still wouldn't work. Please help!
The problem was in the Hashmap Declaration. Initially declaring it as
Hasmap<Object, Object> images;
didn't work in the POJO but after hours of trial and error I deleted the POJO and restarted the system. When I recreated the POJO file, I declared the Hashmap as:
Hashmap images;
Try with this solution:
//First create your object
POJO_MemoriesRVDisplay pojo = new POJO_MemoriesRVDisplay("School", "12345", Your hashmap);
//Create your reference where you want to store your data
private DatabaseReference root = FirebaseDatabase.getInstance().getReference();
//Save your data
root.push().setValue(pojo);
I am trying to store 3 values. The last value is an object which can be accessed by XID. The main ID can be used to get the object.
I can think of two ways to implement this. Which would be a better approach? Also, which is better in terms of thread-safe and faster lookups?
Create a class and add it to the HashMap.
public class TestMap {
private int xid;
private XObject xobject;
public TestMap(int xid, XObject xobject) {
this.xid = xid;
this.object = object;
}
public int getXid() { return xid; }
public XObject getXOBject { return xobject; }
}
map.put(ID, new TestMap(xid, xobject));
Create a nested HashMap
HashMap<id, HashMap<xid, XObject>> map = new HashMap<>();
map.put(id, new HashMap() {{ put(xid, xobject); }} );
When you access it by the main ID, do you first need to specify the XID?
I'm assuming that you do not. In this case, I would make two Maps.
One is a Map<id,object> which you use when looking up by id. The second is a Map<xid,object> which you use when looking up by xid.
There is a interface, that define codes for keyboard. Every button have code.
public interface KeyMap{
private static final int A = 23;
private static final int B = 24;
//other keys
...
}
But my question is: how to get letter (A,B,...) by number (23,24,...).
Something like:
public String getKey(int value);
I tried resolve creating Map, but then there is need to initialize full Map again and again. i thought about Java reflect but cant find right method to do it.
Java Reflection would be possible, but it would be pretty slow, since you have to iterate through all fields to get the one with the correct value.
Another suggestion:
enum KeyCode {
A(23),
...;
private int code;
private static final Map<Integer, KeyCode> keys = new HashMap<Integer, KeyCode>();
static {
for(KeyCode code : values()) {
keys.put(code.code, code);
}
}
private KeyCode(int code) {
this.code = code;
}
public static KeyCode getKey(int code) {
return keys.get(code);
}
}
This allows you to simple do...
KeyCode code = KeyCode.getCode(23);
String name = code.name(); // assuming not null here, should be "A"
This would be pretty fast, etc. But of course, another question would be, if you cannot use an existing framework. As far as I know, Swing already does some key mapping, for example.
Edit:
Ok, as you seem to have to use a pre-defined class, reflection actually seems the only way to go... This method will allow you to get a map of all field names by value.
public static Map<Object, String> getFieldsByValue(Class<?> clz) {
Map<Object, String> map = new HashMap<Object, String>();
// Remember: Class.getField() returns only PUBLIC fields
for (Field field : clz.getFields()) {
// Check if it's a static variable
if (Modifier.isStatic(field.getModifiers())) {
// Add other checks, for example for "integer", if you want.
try {
// field.get(null) returns the value of the field for a static field
map.put(field.get(null), field.getName());
} catch (IllegalArgumentException e) {
// should not happen, as we made sure the field is static
} catch (IllegalAccessException e) {
// should not happen, as we only listed public fields
}
}
}
return map;
}
You can call this method once in a static initializer block (see example above) to create the Map once and then access it, which makes the runtime better:
static {
keyMap = getFieldsByValue(com.vaadin.event.ShortcutAction.KeyCode.class);
// example
String name = keyMap.get(23); // should be "A"
}
This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 7 years ago.
Java supports pass by value (always works on a copy) but when you pass a user defined object then it changes the actual object (kind of pass by reference but no pointer changes), which I understand but why the changeObject2CLEAR method below is actually changing the value of the object ? Instead it has to work on the copy?
import java.util.HashMap;
import java.util.Map;
public class PassBy {
class CustomBean {
public CustomBean() {
}
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return id + ", " + name;
}
}
public Map<Integer, String> changeObject2NULL (Map<Integer, String> m) {
m = null;
return m;
}
public Map<Integer, String> changeObject2CLEAR (Map<Integer, String> m) {
m.clear();
return m;
}
public CustomBean changeCustomObject (CustomBean _e) {
_e.setId(_e.getId() + 1);
return _e;
}
public static void main(String[] args) {
PassBy passby = new PassBy();
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "value");
CustomBean bean = passby.new CustomBean();
bean.setId(1);
bean.setName("arun");
// Initial Value
System.out.println(map.toString());
System.out.println(bean.toString());
System.out.println("-------------------------");
// Pass by value works fine
passby.changeObject2NULL(map);
passby.changeCustomObject(bean);
// Custom object value changes since we pass as the object reference but Map remains with actual value
System.out.println(map.toString());
System.out.println(bean.toString());
System.out.println("-------------------------");
// Testing Pass by value - CANT UNDERSTAND why it changed the object since it has to be pass-by-value and not by ref in this case ??
// Why setting to null not chainging the value but clear does ?
passby.changeObject2CLEAR(map);
System.out.println(map.toString());
}
}
So let me try to help you understand, Java always does pass by value, but I am sure you know that all object instances are actually pointers to those objects. Now when you send an object then you are passing a value of the address of the object. If you do any changes to the object itself (like m.clear()) then it goes to that address, type casts the object and does the operation on it. But if you change the pointer itself, like m = null, then only the copy of the address you are holding is changed.
When you call changeObject2CLEAR
passby.changeObject2CLEAR(map);
you are passing the instance map.
in the method changeObject2CLEAR
public Map<Integer, String> changeObject2CLEAR (Map<Integer, String> m) {
m.clear();
return m;
}
you perform .clear() on that same instance map even though in the method it is called m.
As an exercise in understanding notice that the following method will do the same thing.
public void changeObject2CLEAR (Map<Integer, String> m) {
m.clear();
}
Notice that you don't have to return the Map<Integer, String> m because the map you have access to is the same instance object passed in wherever the method is called.
EDIT: Why does m = null; behave as pass-by-value but m.clear() behave as pass by reference?
When you 'assign' the value null to m you are changing the reference from the previous instance object map to a new memory location that is null.
When you call the .clear() method on the instance object m you are calling the method on the same object that is at the memory location referenced by map, consequently you modify the map object.
AFAIK Java only does passes by value, but the values are actually references
I have this code in my JSP page:
<h:selectManyCheckbox id="chb" value="#{MyBean.selectedCheckBoxes}" layout="pageDirection">
<f:selectItems value="#{MyBean.checkBoxItems}"/>
</h:selectManyCheckbox>
And in my MyBean:
public class MyBean {
public MyBean() {
for (Elem section : sections) {
checkBoxItems.put(section.getName(), section.getObjectID());
}
}
private String[] selectedCheckBoxes;
private Map<String, Object> checkBoxItems = new LinkedHashMap<String, Object>();
public String save() {
//save is not being executed....
return FORWARD;
}
public Map<String, Object> getCheckBoxItems() {
return checkBoxItems;
}
public void setCheckBoxItems(Map<String, Object> checkBoxItems) {
this.checkBoxItems = checkBoxItems;
}
public String[] getSelectedCheckBoxes() {
return selectedCheckBoxes;
}
public void setSelectedCheckBoxes(String[] selectedCheckBoxes) {
this.selectedCheckBoxes = selectedCheckBoxes;
}
}
When I click save it is giving the below message in <t:message for="chb"/>
"chb": Value is not a valid option.
Even though I did not add the required attribute for h:selectManyCheckbox, it is trying to validate or doing something else...
I've changed checkBoxItems variable type(with getter/setters) to List<SelectItem>, but it is not working as well.
What can be the reason, how can I solve it?
PS: I'm using JSF 1.1
You will get this error when the equals() test on a selected item has not returned true for any of the available items. So, when roughly the following happens under JSF's covers:
boolean valid = false;
for (Object availableItem : availableItems) {
if (selectedItem.equals(availableItem)) {
valid = true;
break;
}
}
if (!valid) {
// Validation error: Value is not valid!
}
That can in your particular case only mean that section.getObjectID() does not return a String which is what your selectedCheckboxes is declared to, but a different type or a custom type where equals() is not implemented or broken.
Update as per your comment, the getObjectID() returns Integer. It's thus been treated as String because selectedCheckBoxes is declared as String[]. You should change the following
private String[] selectedCheckBoxes;
private Map<String, Object> checkBoxItems = new LinkedHashMap<String, Object>();
to
private Integer[] selectedCheckBoxes;
private Map<String, Integer> checkBoxItems = new LinkedHashMap<String, Integer>();
and maybe (not sure, can't tell from top of head now) also explicitly supply a converter:
<h:selectManyCheckbox ... converter="javax.faces.Integer">
i didnt find any problem in th code, i thought there is the problem the list u passed to oneManyCheckBox.
hardcode some values in list in getter than check
public Map<String, Object> getCheckBoxItems() {
checkBoxItems.clear();
checkBoxItems.put("aaaa", "aaaa");
checkBoxItems.put("bbbb", "bbbb");
checkBoxItems.put("cccc", "cccc");
checkBoxItems.put("dddd", "dddd");
checkBoxItems.put("eeee", "eeee");
return checkBoxItems;
}