Java RestTemplate десериализация Generi c в Generic - PullRequest
0 голосов
/ 27 мая 2020

Я несколько дней go я пытался использовать restTemplate загрузки Spring, чтобы сделать вызов отдыха для собственного API. По некоторым причинам все ответы такого API имеют оболочку ResponseDTO с полями дерева (не спрашивайте меня, почему, но я не могу изменить этот объект, поэтому избегайте ответов и меняйте ResponseDTO).

githubCode:

https://github.com/Gaboxondo/RestTemplateBuilderTest

Здесь вы можете увидеть пример ответа:

{
  "status": "success",
  "error": null,
  "data": [
    {
      "id": 0,
      "name": "Person_0"
    },
    {
      "id": 1,
      "name": "Person_1"
    }
  ]
}

Как вы В этом случае в Data можно увидеть список людей.

Объект-оболочка выглядит следующим образом

public class ResponseDTO<T> {

    private String status;

    private ArrayList<String> error;

    private T data;

    private static final String SuccessMessage = "success";
    private static final String FailMessage = "fail";

    public ResponseDTO() {
    }

    public ResponseDTO(String status, ArrayList<String> error, T data) {
        this.status = status;
        this.error = error;
        this.data = data;
    }

    public ResponseDTO(String error) {
        this.status = FailMessage;
        ArrayList<String> oneException = new ArrayList<>();
        oneException.add( error );
        this.error = oneException;
        this.data = null;
    }

    public ResponseDTO(T data) {
        this.status = SuccessMessage;
        this.error = null;
        this.data = data;
    }

    public ResponseDTO(boolean statusResponse) {
        if(statusResponse) {
            this.status = SuccessMessage;
        } else {
            this.status = FailMessage;
        }
        this.data = null;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public ArrayList<String> getError() {
        return error;
    }

    public void setError(ArrayList<String> error) {
        this.error = error;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

Для выполнения запроса я создал оболочку для RestTemplate, чтобы упростить ResponseDTO wrapper:

Самый важный метод - метод build ()

public class RestRequestBuilderForResponseDTO<T> {

    private static final Logger LOG = LoggerFactory.getLogger( RestRequestBuilderForResponseDTO.class );

    private static final String SLASH = "/";

    private static final String COLONS = ":";

    private RestTemplate restTemplate;

    private String uri;

    private Map<String, String> headerValues;

    private Object body = null;

    private MultiValueMap<String, String> params;

    private HttpMethod method;

    private String token;

    private Integer timeOut;


    public RestRequestBuilderForResponseDTO(String serverUri) {
        this.restTemplate = new RestTemplate();
        this.uri = serverUri;
        headerValues = new HashMap<>();
        params = new HttpHeaders();
    }

    public RestRequestBuilderForResponseDTO<T> path(int path) {
        this.uri = this.uri + SLASH + path;
        return this;
    }

    public RestRequestBuilderForResponseDTO<T> path(String path) {
        this.uri = this.uri + SLASH + path;
        return this;
    }

    public RestRequestBuilderForResponseDTO<T> path(BigDecimal pathId) {
        this.uri = this.uri + SLASH + pathId.toString();
        return this;
    }

    public RestRequestBuilderForResponseDTO<T> param(String key, BigDecimal value) {
        return param( key, value.toString() );
    }

    public RestRequestBuilderForResponseDTO<T> param(String key, Long value) {
        return param( key, value.toString() );
    }

    public RestRequestBuilderForResponseDTO<T> param(String key, Integer value) {
        return param( key, value.toString() );
    }

    public RestRequestBuilderForResponseDTO<T> header(String key, String value) {
        this.headerValues.put( key, value );
        return this;
    }

    public RestRequestBuilderForResponseDTO<T> bearerToken(String token) {
        this.token = token;
        return this;
    }

    public RestRequestBuilderForResponseDTO<T> body(Object body) {
        this.body = body;
        return this;
    }

    public RestRequestBuilderForResponseDTO<T> timeout(Integer timeOut) {
        this.timeOut = timeOut;
        return this;
    }

    public RestRequestBuilderForResponseDTO<T> post() {
        this.method = HttpMethod.POST;
        return this;
    }

    public RestRequestBuilderForResponseDTO<T> get() {
        this.method = HttpMethod.GET;
        return this;
    }

    public RestRequestBuilderForResponseDTO<T> put() {
        this.method = HttpMethod.PUT;
        return this;
    }

    public RestRequestBuilderForResponseDTO<T> patch() {
        this.method = HttpMethod.PATCH;
        return this;
    }

    public RestRequestBuilderForResponseDTO<T> delete() {
        this.method = HttpMethod.DELETE;
        return this;
    }

    public <T> ResponseEntity<ResponseDTO<T>> build() {
        HttpEntity<Object> httpEntity;
        if (body != null && !method.equals( HttpMethod.GET )) {
            httpEntity = new HttpEntity<>( body, this.headers() );
        } else {
            httpEntity = new HttpEntity<>( this.headers() );
        }

        if (timeOut != null) {
            this.restTemplate.setRequestFactory( setTimeOut( this.timeOut ) );
        }

        LOG.trace( "URL: " + this.uri );
        LOG.trace( "METHOD: " + method.toString() );
        LOG.trace( "HTTP ENTITY: " + httpEntity.toString() );
        ResponseEntity<ResponseDTO<T>> response =
            restTemplate.exchange( this.uri(), method, httpEntity, new ParameterizedTypeReference<ResponseDTO<T>>() {
            } );
        return response;
    }

    public void buildWithoutResponse() {
        HttpEntity<Object> httpEntity;
        if (body != null && !method.equals( HttpMethod.GET )) {
            httpEntity = new HttpEntity<>( body, this.headers() );
        } else {
            httpEntity = new HttpEntity<>( this.headers() );
        }

        if (timeOut != null) {
            this.restTemplate.setRequestFactory( setTimeOut( this.timeOut ) );
        }

        LOG.trace( "HTTP ENTITY: " + httpEntity.toString() );
        restTemplate.exchange( this.uri(), method, httpEntity, Void.class );
    }

    public String getFullTrace() {
        StringBuilder trace = new StringBuilder( "REQUEST, Url: " + uri );
        if (method != null) {
            trace.append( " Method: " ).append( method.toString() );
        }
        if (params != null && !params.isEmpty()) {
            trace.append( " Params: [" );
            for (Map.Entry<String, List<String>> stringListEntry : params.entrySet()) {
                String key = ((Map.Entry) stringListEntry).getKey().toString();
                String value = ((Map.Entry) stringListEntry).getValue().toString();
                String param = "(" + key + "," + value + ")";
                trace.append( param );
            }
            trace.append( "]" );
        }
        if (body != null) {
            trace.append( " Body: [" ).append( body ).append( "]" );
        }
        if (headerValues != null && !headerValues.isEmpty()) {
            trace.append( " Headers: [" );
            for (Map.Entry<String, String> headerValues : headerValues.entrySet()) {
                String key = ((Map.Entry) headerValues).getKey().toString();
                String value = ((Map.Entry) headerValues).getValue().toString();
                String param = "(" + key + "," + value + ")";
                trace.append( param );
            }
            trace.append( "]" );
        }
        if (token != null) {
            trace.append( " TOKEN: [" ).append( token ).append( "]" );
        }
        return trace.toString();
    }

    public String getSimpleTrace() {
        StringBuilder trace = new StringBuilder( "REQUEST, Url: " + uri );
        if (method != null) {
            trace.append( " Method: " ).append( method.toString() );
        }
        if (params != null && !params.isEmpty()) {
            trace.append( " Params: [" );
            for (Map.Entry<String, List<String>> stringListEntry : params.entrySet()) {
                String key = ((Map.Entry) stringListEntry).getKey().toString();
                String value = ((Map.Entry) stringListEntry).getValue().toString();
                String header = "(" + key + "," + value + ")";
                trace.append( header );
            }
            trace.append( "]" );
        }
        return trace.toString();
    }

    private RestRequestBuilderForResponseDTO<T> param(String key, String value) {
        this.params.add( key, value );
        return this;
    }

    private HttpHeaders headers() {
        HttpHeaders headers = new HttpHeaders();
        for (Map.Entry<String, String> entry : headerValues.entrySet()) {
            headers.set( entry.getKey(), entry.getValue() );
        }
        if (token != null) {
            headers.setBearerAuth( token );
        }
        return headers;
    }

    private URI uri() {
        UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl( uri );
        if (!params.isEmpty()) {
            uriComponentsBuilder.queryParams( params );
        }
        return uriComponentsBuilder.build().encode().toUri();
    }

    private ClientHttpRequestFactory setTimeOut(Integer timeout) {
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        clientHttpRequestFactory.setConnectTimeout( timeout );
        return clientHttpRequestFactory;
    }

}

Дело в том, что теперь, когда я делаю запрос:

RestRequestBuilderForResponseDTO<List<PersonDTO>> client =
            new RestRequestBuilderForResponseDTO<List<PersonDTO>>( "http://localhost:8081" ).path( "person" ).path( "all" )
                .get();

        LOG.info( client.getFullTrace() );

        ResponseEntity<ResponseDTO<List<PersonDTO>>> response = client.build();

        ResponseDTO<List<PersonDTO>> body = response.getBody();
        List<PersonDTO> data = body.getData();
        data.get( 0 );
...

если теперь я вижу данные в теле, это означает, что они не были десириализированы в PersonDTO, а вместо этого были выделены LinkedHashMap

enter image description here Есть ли способ правильно выполнить десериализацию?

тем временем я создал метод, в котором я конвертирую значения, но очень утомительно проверять, является ли какой-то объект экземпляром LinkedHashMap или не выполнять преобразование везде, где я его использую.

public static <V, klazz> V transformFromLinkedHaspMapToObject(Object linkedList, Class<V> klazz) {
        ObjectMapper mapper = new ObjectMapper();
        TypeFactory t = TypeFactory.defaultInstance();
        return mapper.convertValue( linkedList, t.constructCollectionType( ArrayList.class, klazz ) );
    }

Спасибо заранее

...