I am trying deserialize the result returned from an API call. However, the result can either contain a Boolean or an array.
If the result is Boolean, the JSON content received in the response looks like:
{
"succeeded": true,
"version": 1.0
}
If the result is an array, the JSON received in the response looks like:
{
"succeeded": {
"current_page": 1,
"per_page": 100,
"results": [
{
"get_info": {
"fieldA": "4198126",
"fieldB": "2016-05-25T22:43:52Z",
"fieldC": "iws-user-cfg-proxy-beta",
"updated_at": "2016-05-25T22:43:52Z"
}
},
{
"get_info": {
"fieldA": "4551542",
"fieldB": "2016-07-27T22:26:27Z",
"fieldC": "silkRoot",
"updated_at": "2016-07-27T22:26:27Z"
}
}
]
},
"version": 1.0
}
I would like to read the value associated with the "succeeded" field. Is there a way I can handle this in the mapping class?
My current mapping class is as below:
public class ServResp {
public final static String TYPE1_EXCEPTION = "Type1Exception";
public final static String TYPE2_EXCEPTION = "Type2Exception";
public final int httpStatusCode;
public final boolan succeeded;
public final String version;
public final String exception;
public final String exceptionMessage;
private ServResp(Builder builder) {
this.httpStatusCode = builder.httpStatusCode;
this.succeeded = builder.succeeded;
this.version = builder.version;
this.exception = builder.exception;
this.exceptionMessage = builder.exceptionMessage;
}
public Builder modify() {
return new Builder(this);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((exception == null) ? 0 : exception.hashCode());
result = prime * result + ((exceptionMessage == null) ? 0 : exceptionMessage.hashCode());
result = prime * result + httpStatusCode;
result = prime * result + (succeeded ? 17 : 19);
result = prime * result + ((version == null) ? 0 : version.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ServResp other = (ServResp) obj;
if (exception == null) {
if (other.exception != null)
return false;
} else if (!exception.equals(other.exception))
return false;
if (exceptionMessage == null) {
if (other.exceptionMessage != null)
return false;
} else if (!exceptionMessage.equals(other.exceptionMessage))
return false;
if (httpStatusCode != other.httpStatusCode)
return false;
if (succeeded != other.succeeded)
return false;
if (version == null) {
if (other.version != null)
return false;
} else if (!version.equals(other.version))
return false;
return true;
}
public static class Builder {
private int httpStatusCode;
private boolean succeeded;
private String version;
private String exception;
private String exceptionMessage;
public Builder() {
}
public Builder(ServResp other) {
this.httpStatusCode = other.httpStatusCode;
this.version = other.version;
this.exception = other.exception;
this.exceptionMessage = other.exceptionMessage;
}
public Builder setHttpStatusCode(int httpStatusCode) {
this.httpStatusCode = httpStatusCode;
return this;
}
public Builder setSucceeded(boolean succeeded) {
this.succeeded = succeeded;
return this;
}
public Builder setVersion(String version) {
this.version = version;
return this;
}
public Builder setException(String exception) {
this.exception = exception;
return this;
}
public Builder setExceptionMessage(String exceptionMessage) {
this.exceptionMessage = exceptionMessage;
return this;
}
public ServResp build() {
return new ServResp(this);
}
}}
If I execute the program the way it is, I get the below error:
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.boolean out of START_OBJECT token
Is there a way to get around this?
You could try changing the type of Builder.succeeded
to Object
, and then add some code to read it later. This sounds like a source of future bugs, but if you don't control the API then it may be your best shot.
public class Foo {
private Object overRiddenJsonType;
public Object getOverRiddenJsonType() {
return overRiddenJsonType;
}
public void setOverRiddenJsonType(Object overRiddenJsonType) {
this.overRiddenJsonType = overRiddenJsonType;
}
}
public class FooConsumer {
public void consumeFoo(Foo foo) {
Boolean b = false;
Bar bar = null;
if (foo.getOverRiddenJsonType() instanceof Boolean) {
b = (Boolean)foo.getOverRiddenJsonType();
// Worry about an NPE from unboxing later...
} else if (foo.getOverRiddenJsonType() instanceof Bar) {
bar = (Bar)foo.getOverRiddenJsonType();
}
// ...
}
}
If, on the other hand, you do control the API, then a better solution would be to restructure your JSON such that success
is always boolean
, and the rest of the data is either a top-level field or a member of results
:
{
"succeeded": true,
"version": 1.0,
"current_page": 1,
"per_page": 100,
"results": [
{
"get_info": {
"fieldA": "4198126",
...
}
]
}