Search code examples
javaspring-bootlinkedhashmap

How to deserialise Json into Immutable Object - Spring, RestTemplate


I have a model that looks like this:

@Data
public class RegistrationRequestDto {

    public final String email;
    public final String username;
    public final String password;
    public final String confirmPassword;
    public final String firstName;
    public final String lastName;
    public final String keycloakId;

    public RegistrationRequestDto(
            String email,
            String username,
            String password,
            String confirmPassword,
            String firstName,
            String lastName,
            String keycloakId) {
        notNull(email, "email must be set");
        notNull(username, "username must be set");
        notNull(password, "password must be set");
        notNull(firstName, "firstName must be set");
        notNull(lastName, "lastName must be set");
        this.email = email;
        this.username = username;
        this.password = password;
        this.confirmPassword = confirmPassword;
        this.firstName = firstName;
        this.lastName = lastName;
        this.keycloakId = keycloakId;
    }
}

Next, I have a method that is calling another service with restTempate.

The result of that call I should save in the model shown above.

I have this piece of code that should call and return result from external service:

RegistrationRequestDto userProfile = new RegistrationRequestDto();
        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            // httpHeaders.set("Authorization", "Bearer " + responseToken.getAccess_token());
            HttpEntity<String> request = new HttpEntity<String>(httpHeaders);

            ResponseEntity<Object> result = restTemplate.exchange(uri, HttpMethod.POST, request, Object.class);
            log.info("{}", result);
            log.info("{}", result.getBody());

            LinkedHashMap<String, Object> map = (LinkedHashMap<String, Object>) result.getBody();

            if (map != null) {
                userProfile.setUserId(map.get("sub").toString());
                userProfile.setGiven_name(map.get("given_name").toString());
                userProfile.setFamily_name(map.get("family_name").toString());
                userProfile.setEmail(map.get("email").toString());
                userProfile.setEmail_verified(map.get("email_verified").toString());
                //userProfile.setPhoto(Optional.ofNullable(map.get("photo").toString()));
            }
    
        } catch (Exception e) {
            e.printStackTrace();
        }
        return userProfile;

So, in this code I am using

if (map != null) {
   userProfile.setUserId(map.get("sub").toString());
   userProfile.setGiven_name(map.get("given_name").toString());
   userProfile.setFamily_name(map.get("family_name").toString());
   userProfile.setEmail(map.get("email").toString());
   userProfile.setEmail_verified(map.get("email_verified").toString());
}

Which I was able to use when variables in RegistrationRequestDto model were just private.

But now as variables are public final I am not sure how to store result in map?


Solution

  • Your dto class must have a fasterxml @JsonCreator on its constructor. something like the following dto:

    import com.fasterxml.jackson.annotation.JsonCreator;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import lombok.Value;
    
    @Value
    public class User {
        public final int id;
        public final String name;
    
        @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
        public User(@JsonProperty("id") int id, @JsonProperty("name") String name) {
            this.id = id;
            this.name = name;
        }
    
    }
    
    

    And in the rest call pass User.class instead of Object.class. Something like below:

    ResponseEntity<User> response = restTemplate.exchange(uri, HttpMethod.GET, HttpEntity.EMPTY, User.class);
    
    User user = response.getBody();
    
    

    Note: @JsonProperty("...") is necessary here. Also @Value is better for your use case than @Data.