Deserializing into a Collection broken in 3.0
Brought to you by:
charliehubbard
Deserializing into a Collection appears to be broken in 3.0.
The following code works in 2.1 but yields an empty Collection in 3.0:
:::java
public static class ListElement {
private String name;
private String value;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
}
public static class JSONResult {
private List<ListElement> elements = new ArrayList<ListElement>();
public List<ListElement> getElements() { return elements; }
public void addElement(ListElement element) { this.elements.add(element); }
}
public static void main(String[] args) {
String str = "{\"elements\":[{\"name\":\"foo\",\"value\":\"bar\"}]}";
JSONResult result = new JSONDeserializer<JSONResult>()
.use(null, JSONResult.class)
.use("elements", ArrayList.class).use("elements.values", ListElement.class)
.deserialize(str);
System.out.println(result.elements);
}
As the milestone update above seems to imply (and I confirmed), this is still broken in 3.1.
I did a little debugging. The issue here would appear to be that
ObjectBinder.bindIntoObject()was changed to include the following check:If that evaluates to
false, the whole method is a no-op, thus skipping collection deserialiization.In my example code above, the "descriptor" is
JSONResult.elements, which is not "writable". However, I cannot see how else to deserialize into a collection (I believe the code snippet above follows the JavaDoc instructions, right?)Right it's not writable because it's been marked private. You simply need to add a setElements() method, which can be protected or private if you like to hide it, in order to allow it to be writeable. This isn't going to be fixed.
Last edit: Charlie Hubbard 2013-09-03
Just curious, why must the setElements() method be provided over just accessing the private instance variable? In another project that deals with mapping data to objects we allow for the direct setting and getting of instance variables with or without setter or getter methods being provided. I'm just curious if there is a technical reason why we can't use reflection to bypass the scope. Perhaps I'm misunderstanding the issue here and I apologize if I'm muddying the waters.
Last edit: Brandon Goodin 2013-09-04
I might add that it is currently working for us in 2.1 without the setter :-)
Yes there are reasons to require they use a setter. Flexjson uses the setter/getter interface to know what properties an object wants to share with the world. The only exception to this rule is public fields. If a field is marked public then the intent of the author is to allow this field to be manipulated directly so we'll serialize / deserialize into that field. However, if a field is marked as private it may or may not be up for serialization/deserialization at the discretion of the author. If a field is marked as private, but has a setter (regardless of the access modifier) then Flexjson sees that as a signal it's ok for it to set that field from JSON. If a field is marked as private and has not setter then that's seen as a read only field, and it's the intent of the author to disallow that field from being manipulated. This could be security reasons for simply it's a derived field that doesn't require us to set it to reconstitute internal state.
Private fields are really private with respect to Flexjson and they aren't assumed to all participate in serialization/deserialization. This isn't a one off decision either. It works the same way for serialization. If there is no getter for a private field we don't include it in the output of the JSON either. This provides the author with encapsulated object state that can be different from the state over JSON. That may be very important for you if your object state is different than JSON state because JSON can't represent all possible object graphs that could be stored in memory for example cycles, etc.
It may have worked in 2.1, but that was a bug that is fixed in 3.0.
Thanks for the explanation. Consistency is definitely critical. I'm thinking we should mark this as closed-wont-fix as there is a pretty easy solution (adding the setter method).