Reputation: 163
I have to call a webservices which return a response like this one below for every object I query (user, customer, provider....)
{"result":
{"code":"OK",
"message": {"id":"1",
"name":"YingYang",
"mail":"[email protected]",
"password":"EDB5FG12BG117KMNJSYHH",
"validated":"1",
"status":"Just fine",
"ranking":"99"}
}
}
The thing is that I need to get the object "user" (or customer or whatever) when 'code' is OK, and a string message/error when 'code' is KO.
I know this structure is quite common for webservices, but I just cant find the way to handle them.
I guess I have to create a custom deserializer to do the job, and this is what I've got so far:
class UserDeserializer extends JsonDeserializer<User>
{
@Override
public User deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException
{
User user = new User();
JsonNode node = jp.getCodec().readTree(jp);
ApiResult result = new ApiResult();
result.code = (String) node.get("result").get("code").toString();
result.message = (String) node.get("result").get("message").toString();
ObjectMapper mapper = new ObjectMapper();
JsonNode messageObj = mapper.readTree(result.message);
Iterator<Map.Entry<String, JsonNode>> it = messageObj.fields();
while (it.hasNext()) {
Map.Entry e = (Map.Entry)it.next();
Field field = null;
try {
field = User.class.getField((String) e.getKey());
if(field.getType().getName().equals("java.lang.String")) {
field.set(user, e.getValue().toString().replace("\"",""));
}
if(field.getType().getName().equals("int")) {
field.set(user, Integer.parseInt(e.getValue().toString().replace("\"","")));
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
return user;
}
}
Can anybody help me out to make this generic for all Api responses for all objects and to generate some kind of message/error when the "code" is "KO"?
Upvotes: 2
Views: 1188
Reputation: 8972
Basically, what you need is something like this:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "code")
@JsonSubTypes({
@JsonSubTypes.Type(value = Result.Error.class, name = "KO"),
@JsonSubTypes.Type(value = User.class, name = "<your string to determine whether the message is a user or not>") })
static class Result {
String code;
Message message;
interface Message {
int id();
// other common properties...
}
static class Error implements Message {
@Override
public int id() {
return -1;
}
}
static class User implements Message {
public int id;
public String name;
// other fields...
@Override
public int id() {
return id;
}
}
}
You don't need to use an interface for Message
, an abstract class would work as well. The idea is that all of your messages(including error) all implements/extends a common type. The annotations tell Jackson how to deserialize the JSON based on the code
property.
Now, in order for this to work, you will have to tell Jackson about the message's type. You can either do it through the code
parameter like the code above and have your JSON look something like this:
{"result":
{"code":"USER",
"message": {"<your user>"}
}
}
// another example
{"result":
{"code":"PROVIDER",
"message": {"<your provider>"}
}
}
Or you can simply specify the type(class) when you deserialize the JSON.
And as always, try to make your fields final/immutable. One simple way of doing that with Jackson is through constructors, annotate your parameters in the constructor and mark it with @JsonCreator
. If you are using Java 8 then this would be extremely useful.
Upvotes: 1