Pobierz listę obiektów JSON za pomocą Spring RestTemplate

199

Mam dwa pytania:

  • Jak zmapować listę obiektów JSON przy użyciu Spring RestTemplate.
  • Jak mapować zagnieżdżone obiekty JSON.

Staram się konsumować https://bitpay.com/api/rates , postępując zgodnie z samouczkiem z http://spring.io/guides/gs/consuming-rest/ .

Karudi
źródło
2
Zastanów się, zobacz tę odpowiedź, zwłaszcza jeśli chcesz użyć listy generics stackoverflow.com/questions/36915823/...
Moesio,

Odpowiedzi:

220

Może w ten sposób ...

ResponseEntity<Object[]> responseEntity = restTemplate.getForEntity(urlGETList, Object[].class);
Object[] objects = responseEntity.getBody();
MediaType contentType = responseEntity.getHeaders().getContentType();
HttpStatus statusCode = responseEntity.getStatusCode();

Kod kontrolera dla RequestMapping

@RequestMapping(value="/Object/getList/", method=RequestMethod.GET)
public @ResponseBody List<Object> findAllObjects() {

    List<Object> objects = new ArrayList<Object>();
    return objects;
}

ResponseEntityjest rozszerzeniem tego, HttpEntityktóry dodaje HttpStatuskod statusu. Używany RestTemplaterównież w @Controllermetodach. W RestTemplatetej klasie zwracany jest przez getForEntity()i exchange().

kamokaze
źródło
To działało jak urok, dziękuję. Może możesz skierować mnie do innych samouczków lub przewodników, które mógłbym przeczytać na ten temat?
Karudi
2
najlepiej poszukać informacji o stosie fragmentów kodu i przykładów lub odwiedzić oficjalną stronę wiosny ...... TblGps [] a = responseEntity.getBody ();
kamokaze
Czy jest to możliwe przy użyciu leków generycznych? tj. moja metoda ma parametr klasy <T rozszerza Foo> i chciałbym uzyskać kolekcję T z metody getForEntity.
Diskutant,
Tak, powinno działać, ale może nie być gotowe, zależnie od wersji Spring / Jackson i typów zajęć. Chodzi o serializację / deserializację generyków - żądanie HTTP nie obchodzi, co jest transportowane.
kamokaze
1
Zobacz także forum.spring.io/forum/spring-projects/android/… .
Benny Bottema,
335

Najpierw zdefiniuj obiekt, aby zatrzymać byt wracający do tablicy. Np

@JsonIgnoreProperties(ignoreUnknown = true)
public class Rate {
    private String name;
    private String code;
    private Double rate;
    // add getters and setters
}

Następnie możesz skorzystać z usługi i uzyskać silnie wpisaną listę za pośrednictwem:

ResponseEntity<List<Rate>> rateResponse =
        restTemplate.exchange("https://bitpay.com/api/rates",
                    HttpMethod.GET, null, new ParameterizedTypeReference<List<Rate>>() {
            });
List<Rate> rates = rateResponse.getBody();

Inne powyższe rozwiązania również będą działać, ale lubię odzyskiwać mocno wpisaną listę zamiast Object [].

Matt
źródło
6
Ten przebieg przebiega bezproblemowo z wiosną 4.2.3 i - jak powiedział Matt - ma dużą zaletę omijania Object []
Marged
@Matt - którego marshalla używasz do zgrupowania Jsona na obiekty Rate? Zgaduję, że tak właśnie się dzieje w czasie restTemplate.exchangemarshallar mapuje wszystkie wartości JSON na pasujące nazwy kluczy jako właściwości w obiekcie Rate. Mam nadzieję, że mój proces myślowy jest prawidłowy.
Nirmal
Idealnie, działa dobrze w Spring Boot 1.4.0. UWOLNIENIE Dzięki
Anand
1
@Nirmal Spring domyślnie korzysta z Jacksona.
Sohaib
1
@SarvarNishonboev obecny ParameterizedTypeReference z springframework.core nadal wydaje się w porządku: docs.spring.io/spring-framework/docs/current/javadoc-api/org/…
fspinnenhirn
75

Dla mnie to zadziałało

Object[] forNow = template.getForObject("URL", Object[].class);
    searchList= Arrays.asList(forNow);

Gdzie Object to klasa, którą chcesz

jonia
źródło
16
Działa to nawet jeśli używasz klasy, a nie Object likeCoupon[] coupons = restTemplate.getForObject( url, Coupon[].class)
lrkwz
1
Może to powodować NPE, jeśli treść odpowiedzi HTTP była pusta ( []ale nie całkowicie pusta). Więc bądź ostrożny i sprawdź null( if (forNow != null)...).
Ruslan Stelmachenko,
1
Zapisałem mój tyłek :) Zastanawiam się, jakiego typu używa Jackson, kiedy Object.classjest określony w metodzie getForObject().
Eric Wang
5

Po wielu testach jest to najlepszy sposób, jaki znalazłem :)

Set<User> test = httpService.get(url).toResponseSet(User[].class);

Wszystko czego potrzebujesz

public <T> Set<T> toResponseSet(Class<T[]> setType) {
    HttpEntity<?> body = new HttpEntity<>(objectBody, headers);
    ResponseEntity<T[]> response = template.exchange(url, method, body, setType);
    return Sets.newHashSet(response.getBody());
}
Romain-p
źródło
Uwaga: wymaga Guava
vphilipnyc
2

Moim wielkim problemem tutaj było zbudowanie struktury Object wymaganej do dopasowania RestTemplate do kompatybilnej klasy. Na szczęście znalazłem http://www.jsonschema2pojo.org/ (pobierz odpowiedź JSON w przeglądarce i użyj jej jako danych wejściowych) i nie mogę tego wystarczająco polecić!

Na północ
źródło
2

faktycznie opracowałem coś funkcjonalnego dla jednego z moich projektów i oto kod:

/**
 * @param url             is the URI address of the WebService
 * @param parameterObject the object where all parameters are passed.
 * @param returnType      the return type you are expecting. Exemple : someClass.class
 */

public static <T> T getObject(String url, Object parameterObject, Class<T> returnType) {
    try {
        ResponseEntity<T> res;
        ObjectMapper mapper = new ObjectMapper();
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
        restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
        ((SimpleClientHttpRequestFactory) restTemplate.getRequestFactory()).setConnectTimeout(2000);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<T> entity = new HttpEntity<T>((T) parameterObject, headers);
        String json = mapper.writeValueAsString(restTemplate.exchange(url, org.springframework.http.HttpMethod.POST, entity, returnType).getBody());
        return new Gson().fromJson(json, returnType);
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

/**
 * @param url             is the URI address of the WebService
 * @param parameterObject the object where all parameters are passed.
 * @param returnType      the type of the returned object. Must be an array. Exemple : someClass[].class
 */
public static <T> List<T> getListOfObjects(String url, Object parameterObject, Class<T[]> returnType) {
    try {
        ObjectMapper mapper = new ObjectMapper();
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
        restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
        ((SimpleClientHttpRequestFactory) restTemplate.getRequestFactory()).setConnectTimeout(2000);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<T> entity = new HttpEntity<T>((T) parameterObject, headers);
        ResponseEntity<Object[]> results = restTemplate.exchange(url, org.springframework.http.HttpMethod.POST, entity, Object[].class);
        String json = mapper.writeValueAsString(results.getBody());
        T[] arr = new Gson().fromJson(json, returnType);
        return Arrays.asList(arr);
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

Mam nadzieję, że to komuś pomoże!

Hamza Jeljeli
źródło
1

Jeśli wolisz listę obiektów, możesz to zrobić w następujący sposób:

public <T> List<T> getApi(final String path, final HttpMethod method) {     
    final RestTemplate restTemplate = new RestTemplate();
    final ResponseEntity<List<T>> response = restTemplate.exchange(
      path,
      method,
      null,
      new ParameterizedTypeReference<List<T>>(){});
    List<T> list = response.getBody();
    return list;
}

I użyj go w ten sposób:

 List<SomeObject> list = someService.getApi("http://localhost:8080/some/api",HttpMethod.GET);

Wyjaśnienia dotyczące powyższego można znaleźć tutaj ( https://www.baeldung.com/spring-rest-template-list ), a ich parafrazę podano poniżej.

„W powyższym kodzie dzieje się kilka rzeczy. Po pierwsze, używamy ResponseEntity jako naszego typu zwracanego, używając go do zawijania listy obiektów, których naprawdę chcemy. Po drugie, wywołujemy RestTemplate.exchange () zamiast getForObject () .

Jest to najbardziej ogólny sposób użycia RestTemplate. Wymaga od nas określenia metody HTTP, opcjonalnego treści żądania i typu odpowiedzi. W tym przypadku używamy anonimowej podklasy ParameterizedTypeReference dla typu odpowiedzi.

Ta ostatnia część pozwala nam przekonwertować odpowiedź JSON na listę obiektów odpowiedniego typu. Kiedy tworzymy anonimową podklasę ParameterizedTypeReference, wykorzystuje ona refleksję do przechwytywania informacji o typie klasy, na który chcemy przekonwertować naszą odpowiedź.

Przechowuje te informacje za pomocą obiektu Type w Javie i nie musimy się już martwić o usunięcie typu ”.

Toofy
źródło
1

Możesz utworzyć POJO dla każdego wpisu, np.

class BitPay{
private String code;
private String name;
private double rate;
}

następnie używając ParameterizedTypeReference z listy BitPay możesz użyć jako:

RestTemplate restTemplate = new RestTemplate();
ResponseEntity<List<Employee>> response = restTemplate.exchange(
  "https://bitpay.com/api/rates",
  HttpMethod.GET,
  null,
  new ParameterizedTypeReference<List<BitPay>>(){});
List<Employee> employees = response.getBody();
Nitin Pawar
źródło
-1

Znalazłem obejście tego postu https://jira.spring.io/browse/SPR-8263 .

Na podstawie tego postu możesz zwrócić taką wpisaną listę:

ResponseEntity<? extends ArrayList<User>> responseEntity = restTemplate.getForEntity(restEndPointUrl, (Class<? extends ArrayList<User>>)ArrayList.class, userId);
Shravan Ramamurthy
źródło
4
To nie zadziała, ponieważ z powodu skasowania nie są przekazywane żadne informacje o parametrach typu getForEntity. (Class<? extends ArrayList<User>>) ArrayList.classDaje również błąd kompilacji „niezgodnych typów”.
Esko Luontola,