简体   繁体   中英

How to deserialize Spring's ResponseEntity with Jackson ObjectMapper (probably with using @JsonCreator and Jackson mixins)

The class ResponseEntity doesn't have a default constructor . Then in order to deserialize it with objectMapper I decided to use the approach given by araqnid in that answer . Shortly - it needs to use mixin feature of Jackson coupled with @JsonCreator.

In my case (with ResponseEntity) it doesn't work out yet due to different reasons.

My test method looks like this:

public static void main(String[] args) throws Exception {
    ResponseEntity<Object> build = ResponseEntity.ok().build();

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.addMixIn(ResponseEntity.class, ResponseEntityMixin.class);

    String s = objectMapper.writeValueAsString(build);
    ResponseEntity<Object> result = objectMapper.readValue(s, ResponseEntity.class);

    System.out.println(result);
}

Firstly, I tried to use the shortest constructor for mixin:

public abstract static class ResponseEntityMixin {
    @JsonCreator
    public ResponseEntityMixin(@JsonProperty("status") HttpStatus status) {
    }
}

In this case I got an assertion error since ResponseEntity has this line of code inside it's constructor:

Assert.notNull(status, "HttpStatus must not be null");

Then I switched the mode of @JsonCreator to DELEGATING but in this case I got another exception:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `org.springframework.http.HttpStatus` from String "headers": value not one of declared Enum instance names: [UPGRADE_REQUIRED, UNAVAILABLE_FOR_LEGAL_REASONS, SERVICE_UNAVAILABLE, CHECKPOINT, LOCKED, METHOD_FAILURE, FAILED_DEPENDENCY, UNPROCESSABLE_ENTITY, PROCESSING, PROXY_AUTHENTICATION_REQUIRED, METHOD_NOT_ALLOWED, GONE, MULTIPLE_CHOICES, GATEWAY_TIMEOUT, ACCEPTED, TEMPORARY_REDIRECT, INTERNAL_SERVER_ERROR, URI_TOO_LONG, LOOP_DETECTED, PAYLOAD_TOO_LARGE, EXPECTATION_FAILED, MOVED_TEMPORARILY, REQUEST_ENTITY_TOO_LARGE, NOT_EXTENDED, CREATED, RESET_CONTENT, BAD_GATEWAY, CONFLICT, VARIANT_ALSO_NEGOTIATES, NETWORK_AUTHENTICATION_REQUIRED, NOT_FOUND, LENGTH_REQUIRED, INSUFFICIENT_SPACE_ON_RESOURCE, NO_CONTENT, OK, FOUND, SEE_OTHER, BANDWIDTH_LIMIT_EXCEEDED, REQUEST_HEADER_FIELDS_TOO_LARGE, PERMANENT_REDIRECT, NOT_ACCEPTABLE, MOVED_PERMANENTLY, REQUEST_TIMEOUT, UNAUTHORIZED, USE_PROXY, IM_USED, ALREADY_REPORTED, PARTIAL_CONTENT, PRECONDITION_FAILED, REQUEST_URI_TOO_LONG, BAD_REQUEST, INSUFFICIENT_STORAGE, CONTINUE, NON_AUTHORITATIVE_INFORMATION, REQUESTED_RANGE_NOT_SATISFIABLE, UNSUPPORTED_MEDIA_TYPE, I_AM_A_TEAPOT, HTTP_VERSION_NOT_SUPPORTED, SWITCHING_PROTOCOLS, NOT_MODIFIED, NOT_IMPLEMENTED, TOO_MANY_REQUESTS, DESTINATION_LOCKED, PAYMENT_REQUIRED, FORBIDDEN, PRECONDITION_REQUIRED, MULTI_STATUS]
 at [Source: (String)"{"headers":{},"body":null,"statusCode":"OK","statusCodeValue":200}"; line: 1, column: 2]

Also I tried to use the all-arguments constructor of ResponseEntity but it was also unsuccessful because of problems with MultiValueMap deserialization (but I believe that if I fixed it then it would finally bring me to the same problem as described above).

Could anyone help me solving this problem? Maybe it's impossible to use mixins in this case at all?

If you know the another approaches how to deserialize ResponseEntity with Jackson - please give them too.

Here is the source code of my tests: https://github.com/amseager/responseentitydeserialization

Using MixIns is a good way to solve it:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

public class Main {
    public static void main(String[] args) throws Exception {
        ResponseEntity<Object> entity = ResponseEntity
                .ok()
                .header("header", "value")
                .body("Everything is OK!");

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.addMixIn(ResponseEntity.class, ResponseEntityMixin.class);
        objectMapper.addMixIn(HttpStatus.class, HttpStatusMixIn.class);

        String json = objectMapper.writeValueAsString(entity);
        TypeReference ref = new TypeReference<ResponseEntity<Object>>() {
        };
        ResponseEntity<Object> result = objectMapper.readValue(json, ref);
        System.out.println(result);
        System.out.println(result.equals(entity));
    }
}

@JsonIgnoreProperties(ignoreUnknown = true)
class ResponseEntityMixin {
    @JsonCreator
    public ResponseEntityMixin(@JsonProperty("body") Object body,
                               @JsonDeserialize(as = LinkedMultiValueMap.class) @JsonProperty("headers") MultiValueMap<String, String> headers,
                               @JsonProperty("statusCodeValue") HttpStatus status) {
    }
}

class HttpStatusMixIn {

    @JsonCreator
    public static HttpStatus resolve(int statusCode) {
        return HttpStatus.NO_CONTENT;
    }
}

Above code prints:

<200 OK OK,Everything is OK!,[header:"value"]>

and
true which means source and deserialised objects are the same.

You need to add ResponseEntityDecoder to able to parse ResponseEntity :

Feign.Builder builder = Feign.builder()
            .encoder(new JacksonEncoder(objectMapper))
            .decoder(new ResponseEntityDecoder(new JacksonDecoder(objectMapper)))

Then it should work

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM