Spring RestTemplate и общие типы ParameterizedTypeReference коллекции, такие как List <T>

Для класса абстрактного контроллера требуется список объектов из REST. При использовании Spring RestTemplate его не сопоставление с требуемым классом вместо этого возвращает Linked HashMAp

 public List<T> restFindAll() {

    RestTemplate restTemplate = RestClient.build().restTemplate();
    ParameterizedTypeReference<List<T>>  parameterizedTypeReference = new ParameterizedTypeReference<List<T>>(){};
    String uri= BASE_URI +"/"+ getPath();

    ResponseEntity<List<T>> exchange = restTemplate.exchange(uri, HttpMethod.GET, null,parameterizedTypeReference);
    List<T> entities = exchange.getBody();
    // here entities are List<LinkedHashMap>
    return entities;

}

Если я использую

ParameterizedTypeReference<List<AttributeInfo>>  parameterizedTypeReference = 
    new ParameterizedTypeReference<List<AttributeInfo>>(){};
    ResponseEntity<List<AttributeInfo>> exchange =
  restTemplate.exchange(uri, HttpMethod.GET, null,parameterizedTypeReference);

Он отлично работает. Но не может помещать все подклассы, любое другое решение.

Ответ 1

Не удалось найти решение из Spring, поэтому я сделал это с ParameterizedTypeReference в HashMap как

 public final static HashMap<Class,ParameterizedTypeReference> paramTypeRefMap = new HashMap() ;
 static {
    paramTypeRefMap.put(AttributeDefinition.class, new ParameterizedTypeReference<List<AttributeDefinition>>(){} );
    paramTypeRefMap.put(AttributeInfo.class, new ParameterizedTypeReference<List<AttributeInfo>>(){} );
 }

и использовал его

ParameterizedTypeReference parameterizedTypeReference = paramTypeRefMap.get(requiredClass);
ResponseEntity<List> exchange = restTemplate.exchange(uri, HttpMethod.POST, entity, parameterizedTypeReference);

Ответ 2

Я работал над этим, используя следующий общий метод:

public <T> List<T> exchangeAsList(String uri, ParameterizedTypeReference<List<T>> responseType) {
    return restTemplate.exchange(uri, HttpMethod.GET, null, responseType).getBody();
}

Тогда я мог бы позвонить:

List<MyDto> dtoList = this.exchangeAsList("http://my/url", new ParameterizedTypeReference<List<MyDto>>() {});

Это обременяло моих абонентов необходимостью указывать ParameterizedTypeReference при вызове, но означало, что мне не нужно было сохранять статическое отображение типов, как в ответе vels4j.

Ответ 3

Используя ParameterizedTypeReference для List<Domain>, когда Domain является явным классом, этот ParameterizedTypeReference работает хорошо, например так:

@Override
public List<Person> listAll() throws Exception {
    ResponseEntity<List<E>> response = restTemplate.exchange("http://example.com/person/", HttpMethod.GET, null,
            new ParameterizedTypeReference<List<Person>>() {});
    return response.getBody();
}

Однако, если метод listAll используется в общем виде, этот список должен сам параметризоваться. Лучший способ, который я нашел для этого:

public abstract class WebServiceImpl<E> implements BaseService<E> {

    private Class<E> entityClass;

    @SuppressWarnings("unchecked")
    public WebServiceImpl() {
        this.entityClass = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass())
            .getActualTypeArguments()[0];
    }


    @Override
    public List<E> listAll() throws Exception {
        ResponseEntity<List<E>> response =  restTemplate.exchange("http://example.com/person/", HttpMethod.GET, null,
                new ParameterizedTypeReference<List<E>>() {
                    @Override
                    public Type getType() {
                        Type type = super.getType();
                        if (type instanceof ParameterizedType) {
                            Type[] responseWrapperActualTypes = { entityClass };
                            ParameterizedType responseWrapperType = new ParameterizedTypeImpl(List.class,
                                    responseWrapperActualTypes, null);
                            return responseWrapperType;
                        }
                        return type;
                    }
                });
        return response.getBody();
    }
}

Ответ 4

Самым простым решением для меня является определение объекта MyOperationResult, содержащего список, который вы ожидаете, в качестве поля и использование restTemplate.getForObject для получения этого результата.