GSON not parsing booleans (always false) - java

I am using Retrofit in order to get a JSON document. Problem is, all booleans are always false.
The response looks like this:
{
"gender":[0,1],
"age":[20,30],
"likesLeaveHome":false,
"likesSport":false,
"likesCulture":false,
"likesTraveling":false
...
}
I am calling the retrofit method with
onResponse(Call<SearchProfile> call, Response<SearchProfile> response)
And the class of SearchProfile which the response should be parsed to looks like that:
public class SearchProfile {
public ArrayList<Integer> gender = new ArrayList<>(); // works fine
public ArrayList<Integer> age = new ArrayList<>(); // works fine
...
public Boolean likesLeaveHome = true; // always false
#SerializedName("likesSport")
public boolean likesSport = true; // always false
#SerializedName("likesCulture")
public Boolean likesCulture; // always false
#SerializedName("likesTraveling")
public Boolean mLikesTraveling; // always false
public boolean isLikesTraveling() {
return mLikesTraveling;
}
public void setLikesTraveling(boolean likesTraveling) {
mLikesTraveling = likesTraveling;
}
}
As you can see, it is a simple pojo class. Lists like "gender" and "age" work perfectly fine. Still, the booleans can't be set. (This is especially strange since sending this object via Retrofit sends exactly this document so GSON surely knows booleans).
As shown in the snipped, I also tried other methods like giving the Boolean wrapper class as type instead of boolean.
I also used a #SerializeName annotation or getter and setts methods etc.
Still all booleans are always false. Even if I declare them default as true (so GSON always seems to overwrite this value with false).
Hope someone has a good idea!

The JSON you are parsing in your example has all the values as false. When parsing, GSON will use reflection to overwrite field values for a class, meaning that whatever value is parsed from JSON will be the value of the field, regardless of how it is initialized in the class.
Here is a sample response that will cause the fields to be set to true. Again, it's just up to whatever JSON you are parsing.
{
"gender":[0,1],
"age":[20,30],
"likesLeaveHome":true,
"likesSport":true,
"likesCulture":true,
"likesTraveling":true
...
}

Related

Inconsistent Jackson "Unrecognized field" due to capitalization

Jackson seems to have a problem when the second letter of a field name is capitalized.
Take a map of values:
aaBoolean, true // works
aBoolean, false // fails
anInt, 0 // works
aString, "a" // fails
I used Jackson's ObjectMapper.convertValue(map) to create a Java object. Here's a snippet of Java code:
private boolean aaBoolean; // GOOD
public boolean getAaBoolean() { return aaBoolean; }
public void setAaBoolean(boolean value) { this.aaBoolean=value;}
private boolean aBoolean; // FAILS!!! Jackson "Unrecognized field"
public boolean getABoolean() { return aBoolean; }
public void setABoolean(boolean value) { this.aBoolean=value;}
I get an error message with all 18 fields. Note the camel case fails when the capital is the second letter:
Unrecognized field "aBoolean" (class Test), not marked as ignorable
(18 known properties: "anInt", "anullableBoolean", "aboolean", "aaBoolean",
"lastModifiedDate", "adate", "anullableDate", "astring", "anullableString",
"createdDate", "anullableFloat", "id", "along", "anullableLong", "createdBy",
"anullableInt", "lastModifiedBy", "afloat"])
If I change aBoolean to aaBoolean, that passes and Jackson fails on aString.
Per other Stack Overflow posts, I've verified that the field name and getter/setter match (aBoolean is getABoolean() and aaBoolean is getAaBoolean()).
If it matters, here's how the ObjectMapper was created:
ObjectMapper mapper = new ObjectMapper();
mapper.setTimeZone(TimeZone.getTimeZone("CST"));
I can post the full code but I think the above is enough.
I can modify my variable names to get around this, but now I'm curious - is this a bug or am I missing something about how Jackson handles name conversion?
It looks like the default Jackson behavior through v2.9 is to lowercase any leading upper case getter/setter methods. So "getURLtoServer" becomes "urltoServer".
Jackson source code executing this here:
https://github.com/FasterXML/jackson-databind/blob/2.9/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java#L246
However, the JavaBean spec says to not change any casing if the first two characters are uppercase. So the JavaBean of "getURLtoServer" would be "URLtoServer".
Jackson introduced MapperFeature.USE_STD_BEAN_NAMING as an option to enforce this part of the JavaBean spec. However, it looks like that's being removed in v3.x when it becomes the default behavior:
https://github.com/FasterXML/jackson-databind/issues/1772
So for your case, it looks like you can either have the JSON string "aboolean" without using USE_STD_BEAN_NAMING, or else you can have the JSON string "ABoolean" with using USE_STD_BEAN_NAMING.
The other option is to manually specify what you want:
#JsonProperty("aBoolean")
public boolean getABoolean() { return aBoolean; }

Is there a way to "expect" instance of certain Java class in JavaScript code?

Assume that I have Foo.class in Java:
public class Foo {
public int id;
public String data;
}
And that I have Foo "class" in JavaScript:
function Foo(id, data) {
this.id = id;
this.data = data;
}
Also, assume that I have Java controller that returns instance of Foo.class as a response to a REST request. In my JavaScript (AngularJS) code the request is sent as:
$http.get(url + 'bar/get-foo/')
.success(function (response) {
var foo = new Foo(response.id, response.data);
logger.info("SUCCESS: /get-foo");
})
.error(function (error_message) {
logger.error(error_message)
});
And it works. But is there a way to avoid passing every property from response to Foo constructor (some sort of expecting the Foo object, or casting it into a Foo object)?
I tried using Object.create(Foo, response) but I get TypeError: Property description must be an object: true
Of course there's always a possibility of refactoring the JavaScript side Foo constructor into:
function Foo(foo) {
this.id = foo.id;
this.data = foo.data;
}
But, that would require refactor of large portion of the codebase.
Thanks for your time. I appreciate it!
PS: For those who wonder why do I need this: It's not a problem with small classes like Foo, but some responses are instances of a much larger classes (with over a dozen of fields), which are not under my control.
EDIT: I accepted Chichozell's answer simply because it requires the least amount of work. Robin's and jonnyknowsbest's answers also work, (and will work for pure JavaScript, unlike Chichozell's answer, which is AngularJS specific). Haven't tried Laurentiu L.'s answer, but looks like it should also work.
Anyway this is A solution (not THE solution):
.success(function (response) {
var foo = new Foo();
angular.extend(foo, response); // angular.merge() for "deep-copy"
//...
}
Big thanks to everyone who answered/commented/edited in this thread.
If you want to keep your Java thinking on javascript, try using angular.extend(), which will "copy" the properties of an object to another
this = angular.extend(this, response)
In the foo function, or directly on the controler:
Foo = angular.extend(Foo, response)
You can do something like this to "deserialise" the JSON you receive back as the response to an initialised object:
function JSONToObj(jsondata) {
var json = JSON.parse(jsondata);
var name = null;
for(var i in json) { //Use first property as name
name = i;
break;
}
if (name == null)
return null;
var obj = new window[name]();
for(var i in json[name])
obj[i] = json[name][i];
return obj;
}
This assumes that the constructor exists in the global scope and that the response is JSON-formatted as such:
{
"Foo": {
"id": "the id",
"data": "the data"
}
}
You can make it pretty generic if you want to. And there wouldn't be too much refactoring to do, and this solution would ease your future changes to both classes.
You may change your Foo javascript object to an Angular JS service and inject it wherever you need it. This way you can have your data available globally. It's better than that local variable foo.
yourApp.factory('Foo',
function () {
//set a default or just initialize it
var fooObject= {};
return {
getId: function () { return fooObject.id; },
getData: function() { return fooObject.data;},
setId: function(newId){fooObject.id = newId},
setData: function(newData){fooObject.data=newData;},
initializeFromObject : function(response){
for (var prop in response){
fooObject[prop] = response[prop];
}
}
};
}
);
You can also make the creation of new services safer with methods like hasAllProperties (by iterating through the object's properties, whether it is an array or object). ; hasNullValues and so on.
Hope this helps and you see it's value.
You could also do something like this jsFiddle to achieve the structure you describe. The createObject function couold look something like the following code snippet.
function createObject(response, toCreate){
var newObject = new toCreate();
for(var attr in response){
if(newObject.hasOwnProperty(attr)){
newObject[attr] = response[attr];
}
}
return newObject;
}
Where you have createObject which takes a js object with the same attributes as your function as the response parameter, and a function (the object you want to create) as the toCreate parameter.
See the jsFiddle console log output, and you see that it works.
You could also, as seen in the jsFiddle, remove the check for hasOwnProperty to set the all attributes from the response regardless if the javascript function has them defined.

Partial matching of request with RESTITO

Can I match a REST-request content without a exact match of content with test framework RESTITO? Lets say I have a timestamp from now in my request but I don't want to match with this specific value (I probably don't know it anyway)?
If your URL looks like
http://example.com/api/endpoint?weight=100&timestamp=1413108487
then you can to the following:
match(get("/api/endpoint"), parameter("weight", "100"))
It will just ignore all the timestamps. If timestamp is part of URI:
http://example.com/api/endpoint/1413108487/bla
then you can use matchesUri() e.g.:
match(method(Method.GET), matchesUri(new Regexp("/api/endpoint/[0-9]+/bla")))
And of course you always can write a custom condition, where you can do any checks on the request you want and return a boolean e.g.:
Predicate<Call> uriEndsWithA = new Predicate<Call>() {
#Override
public boolean apply(final Call input) {
return input.getUri().endsWith("a");
}
};
whenHttp(server).match(custom(uriEndsWithA)).then(ok());

Methods vs Method assigned to variables in Java

I'm sort of confused, I guess this question is just a matter of preference, I just want to understand the difference of the following code.
if (IsRegistered() == true) ...
public boolean IsRegistered()
{
private boolean status = false;
// blah blah code here
return status;
}
vs
isRegistered = IsRegistered();
if (isRegistered)
I know both would work, I'm not being pedantic but I just want to understand so I would know my way around.
if (isRegistered() == true) ...
This is verbose since you know if it returns true it will do it, if not, it wont. So its the same as doing:
if (isRegistered()) ...
What it does, its just getting the returning boolean value from the method and checking the condition in the if statement.
Now if you wanted to check the boolean value again, you would need to re-call the method (which may have to do something complex to return that value), BUT if you assign it to a variable first and then check the condition, like this:
boolean isRegistered = isRegistered();
if (isRegistered)...
Later on the code you can just do it again without calling that method again.
if (isRegistered)... // n lines later.
hence, avoiding executing the process again.
At the end of the day, it pretty much depends on what you need to do.
When you invoke a method which has a non-void return type, the method itself resolves to a value the same way that using a variable does. You can either use that value directly or assign it to a variable and use that.
Just use:
if ( IsRegistered() )
It's the most readable code. Having a variable to "unbox" the method will not do any good; the compiler picks it up and replaces it into the control code itself in an internal optimization pass.
Also, the performance of your IsRegistered() method, when the self-optimization of machines is put aside, depends on how your "my code here" works:
source : {
private boolean status = false;
// blah blah code here
return status;
}
optimization-passed : {
return false; // When internal code does not modify "status"
preturn _status; // When internal code modifies "status"
}

Deserialise Json to class with Generic fields

I currently have the following class
public class Setting<T> {
private T value;
private T defaultValue;
/* getters and setters */
}
The trouble I'm having is deserializing from the Json, for example, having a Setting will work fine if the Json is
{ \"value\": true, \"defaultValue\": false }
.. but, it will also accept
{ \"value\": \"true\", \"defaultValue\": \"false\" }
Even tho the field is of Setting. From what I understand, Java strips away all the generic info, so jackson cannot look it up at runtime. I have tried the following
Field currentSettingField = currentSettingsObject.getClass().getDeclaredField("mySetting");
if (currentSettingField != null) {
JavaType settingType = mapper.getTypeFactory().constructType(settingField.getGenericType(), Setting.class);
Setting setting = objectMapper.readValue(currentSettingNode.toString(), settingType);
}
Which has been somewhat successful, however I can still do things like converting a String or Integer value from Json to a type of Setting. Am I doing something wrong here, or would I be better just have a simple marker interface, then derive each type of setting I want from it?
From what I see from Jackson's code, it tries it's best to guess the value for the boolean and matches strings:
// And finally, let's allow Strings to be converted too
if (t == JsonToken.VALUE_STRING) {
String text = jp.getText().trim();
if ("true".equals(text)) {
return Boolean.TRUE;
}
if ("false".equals(text)) {
return Boolean.FALSE;
}
if (text.length() == 0) {
return (Boolean) getEmptyValue();
}
throw ctxt.weirdStringException(text, _valueClass, "only \"true\" or \"false\" recognized");
}
So if you change you generic type to Integer or provide an incorrect input i.e. 'tru', it won't able to match it.
"Java strips away all the generic info"
AFAIK, Java can do this, but it doesn't remove type attributes information from classes. It doesn't use them however during runtime. This make possible for libraries like Jackson are able to use it.

Categories