GVArt GVArt - 4 months ago 33
Java Question

How RestTemplate parse REST response

While developing spring REST Client, appear a a problem:


  1. Exists a web services.
    Example of REST response.






{"return": [
{
"admin": false,
"alias": "",
"email": "",
"emailId": {"value": 0},
"groups": [],
"id": {"value": 1},
"locked": false,
"loggedInCount": 0,
"master": true,
"sms": "",
"smsId": {"value": 0},
"type": "POWER",
"username": "NGCP"
},
{
"admin": false,
"alias": "",
"email": "",
"emailId": {"value": 0},
"groups": [{"value": 2}],
"id": {"value": 3},
"locked": false,
"loggedInCount": 0,
"master": false,
"sms": "",
"smsId": {"value": 0},
"type": "POWER",
"username": "POLICY"
}
]
}


Model class to save User:



@JsonIgnoreProperties(ignoreUnknown = true)
public class User {

public User(){

}

private boolean admin;

private String alias;

private String email;

private String emailId;

private ArrayList<String> groups;

private String id;

private boolean locked;

private int loggedInCount;

private boolean master;

private String sms;

private String smsId;

private String type;

private String userName;

//getter and setters
}


Now I'm using "RestTemplate" to get result.



RestTemplate restTemplate = new RestTemplate();
ResponseEntity<User[]> response = restTemplate.exchange(URL_GET,HttpMethod.GET,request, User[].class);


And get error. I know that is because main key is "result" but can I specify from where should restTemplate parse this JSON?

And it's possible to indicate on fileds liks "emailId" to get direct value? some templates?

Or maybe what I talk is to hard to do and I have manually to parse this JSON with JsonParser?

Answer
  1. As of the "main key is result":

    a. I would create a wrapper class for the actual payload if you deal with just one of this kind of web service:

    public class Return{
        // Class property cannot be called "return" because it is Java reserved name.
        @JsonProperty("return")
        private User[] array;
        .... getter and setter
    }
    

    b. If you deal with multiple webservices where actual payload is in "return" field I would create a generic wrapper class :

    public class Return<T>{
        // Class property cannot be called "return" because it is Java reserved name.
        @JsonProperty("return")
        private T[] array;
        .... getter and setter
    }
    

    Call to RestRemplate:

    ResponseEntity<Return<User>> response = restTemplate.exchange(URL_GET, 
            HttpMethod.GET, request, new ParameterizedTypeReference<Return<User>>(){});
    User[] usersArray = response2.getBody().getArray();
    
  2. As of the property value in JSON attribute called "value" I would create two custom JsonDeserializer(s): one for single value and one for array of values and annotate each property with @JsonDeserialize where it applies:

    Single value deserializer:

    public class StringValueDeserializer  extends JsonDeserializer<String>{
    
        @Override
        public String deserialize(JsonParser parser, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            ObjectCodec codec = parser.getCodec();
            TreeNode node = codec.readTree(parser);
            JsonNode value = (JsonNode)node.get("value");
    
            if (value != null){
                return value.asText();
            }
            return null;
        }
    }
    

    Array of values derializer:

    public class StringArrayValueDeserializer  extends JsonDeserializer<List<String>>{
    
        @Override
        public List<String> deserialize(JsonParser parser, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
    
            List<String> ret = new ArrayList<>();
    
            ObjectCodec codec = parser.getCodec();
            TreeNode node = codec.readTree(parser);
    
            if (node.isArray()){
                for (JsonNode n : (ArrayNode)node){
                    JsonNode value = n.get("value");
                    if (value != null){
                        ret.add(value.asText());
                    }
                }
            }
            return ret;
        }
    }
    

    Here you are new User.class:

    public class User {
    
        private boolean admin;
    
        private String alias;
    
        private String email;
    
        @JsonDeserialize(using = StringValueDeserializer.class)
        private String emailId;
    
        @JsonDeserialize(using = StringArrayValueDeserializer.class)
        private ArrayList<String> groups;
    
        @JsonDeserialize(using = StringValueDeserializer.class)
        private String id;
    
        private boolean locked;
    
        private int loggedInCount;
    
        private boolean master;
    
        private String sms;
    
        @JsonDeserialize(using = StringValueDeserializer.class)
        private String smsId;
    
        private String type;
    
        private String username;
        .... getter and setter
    }
    

Good luck!