Dapatkan daftar objek JSON dengan Spring RestTemplate

199

Saya punya dua pertanyaan:

  • Cara memetakan daftar objek JSON menggunakan Spring RestTemplate.
  • Cara memetakan objek JSON bersarang.

Saya mencoba untuk mengkonsumsi https://bitpay.com/api/rates , dengan mengikuti tutorial dari http://spring.io/guides/gs/consuming-rest/ .

Karudi
sumber
2
Pertimbangkan untuk melihat jawaban ini, khususnya jika Anda ingin menggunakan daftar obat generik stackoverflow.com/questions/36915823/…
Moesio

Jawaban:

220

Mungkin dengan cara ini ...

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

Kode pengontrol untuk RequestMapping

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

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

ResponseEntityadalah perpanjangan dari HttpEntityyang menambahkan HttpStatuskode status. Digunakan dalam metode RestTemplatejuga @Controller. Di RestTemplatekelas ini dikembalikan oleh getForEntity()dan exchange().

kamokaze
sumber
Itu bekerja seperti pesona, terima kasih. Mungkin Anda bisa mengarahkan saya ke beberapa tutorial atau panduan lain yang bisa saya baca tentang topik ini?
Karudi
2
terbaik untuk melihat di sini di stackoverflow untuk beberapa potongan kode dan contoh atau kunjungi situs web musim semi resmi ...... TblGps [] a = responseEntity.getBody ();
kamokaze
Apakah mungkin menggunakan generik ini? yaitu metode saya memiliki parameter Kelas <T extends Foo> dan saya ingin mendapatkan koleksi T dari metode getForEntity.
Diskutant
Ya itu harus bekerja, tetapi mungkin tidak di luar kotak tergantung pada versi pegas / jackson Anda dan jenis kelas Anda. Ini semua tentang serialisasi / deserializing generik - Permintaan http sendiri tidak peduli apa yang diangkut.
kamokaze
335

Pertama-tama tentukan objek yang menahan entitas yang akan kembali dalam array .. misalnya

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

Kemudian Anda dapat menggunakan layanan ini dan mendapatkan daftar yang sangat diketik melalui:

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

Solusi lain di atas juga akan berfungsi, tapi saya suka mendapatkan daftar yang sangat diketik kembali daripada Object [].

Mat
sumber
6
Proses ini berjalan lancar dengan Spring 4.2.3 dan - seperti yang dikatakan Matt - memiliki keuntungan besar untuk menghindari Object []
Marged
@ Matt - marshaller mana yang Anda gunakan untuk marshal json ke objek Rate? Saya menduga itulah yang terjadi di sini, pada saat restTemplate.exchangea marshallar memetakan semua nilai json ke nama kunci yang cocok sebagai properti di objek Rate. Semoga proses pemikiran saya benar.
Nirmal
Sempurna, berfungsi dengan baik di Spring Boot 1.4.0.RELEASE Terima kasih
Anand
1
@Nirmal Spring menggunakan Jackson secara default, saya percaya.
Sohaib
1
@SarvarNishonboev ParameterizedTypeReference saat ini dari springframework.core masih tampak baik: docs.spring.io/spring-framework/docs/current/javadoc-api/org/…
fspinnenhirn
75

Bagi saya ini berhasil

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

Di mana Object adalah kelas yang Anda inginkan

yonia
sumber
16
Ini berfungsi bahkan jika Anda menggunakan kelas dan bukan Object likeCoupon[] coupons = restTemplate.getForObject( url, Coupon[].class)
lrkwz
1
Ini dapat menyebabkan NPE jika badan respons HTTP kosong (tidak []tetapi benar-benar kosong). Jadi hati-hati dan periksa null( if (forNow != null)...).
Ruslan Stelmachenko
1
Menyelamatkan pantatku :) Ingin tahu jenis apa yang digunakan oleh Jackson, ketika Object.classditentukan dalam metode getForObject().
Eric Wang
5

Setelah beberapa tes, ini adalah cara terbaik yang saya temukan :)

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

Yang kamu butuhkan di sana

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
sumber
Catatan: ini membutuhkan Guava
vphilipnyc
2

Masalah besar saya di sini adalah untuk membangun struktur Objek yang diperlukan untuk mencocokkan RestTemplate ke Kelas yang kompatibel. Untungnya saya menemukan http://www.jsonschema2pojo.org/ (dapatkan respons JSON di browser dan menggunakannya sebagai masukan) dan saya tidak bisa merekomendasikan ini cukup!

ke atas
sumber
2

saya benar-benar mengeluarkan sesuatu yang fungsional untuk salah satu proyek saya sebelum dan di sini adalah kode:

/**
 * @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;
    }
}

Saya harap ini akan membantu seseorang!

Hamza Jeljeli
sumber
1

Jika Anda lebih suka Daftar Objek, salah satu cara untuk melakukannya adalah seperti ini:

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;
}

Dan gunakan seperti ini:

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

Penjelasan untuk hal di atas dapat ditemukan di sini ( https://www.baeldung.com/spring-rest-template-list ) dan diparafrasekan di bawah ini.

"Ada beberapa hal yang terjadi dalam kode di atas. Pertama, kami menggunakan ResponseEntity sebagai tipe pengembalian kami, menggunakannya untuk membungkus daftar objek yang benar-benar kami inginkan. Kedua, kami memanggil RestTemplate.exchange () alih-alih getForObject () .

Ini adalah cara paling umum untuk menggunakan RestTemplate. Ini mengharuskan kami untuk menentukan metode HTTP, badan permintaan opsional, dan jenis respons. Dalam hal ini, kami menggunakan subkelas anonim dari ParameterizedTypeReference untuk tipe respons.

Bagian terakhir inilah yang memungkinkan kita untuk mengubah respons JSON menjadi daftar objek yang merupakan tipe yang sesuai. Saat kami membuat subkelas anonim dari ParameterizedTypeReference, ia menggunakan refleksi untuk menangkap informasi tentang tipe kelas yang ingin kami konversi responsnya.

Itu berpegang pada informasi ini menggunakan objek Type Java, dan kita tidak perlu lagi khawatir tentang penghapusan tipe. "

Toofy
sumber
1

Anda dapat membuat POJO untuk setiap entri seperti,

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

lalu gunakan ParameterizedTypeReference of List of BitPay yang dapat Anda gunakan sebagai:

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
sumber
-1

Saya menemukan pekerjaan sekitar dari posting ini https://jira.spring.io/browse/SPR-8263 .

Berdasarkan posting ini Anda dapat mengembalikan daftar yang diketik seperti ini:

ResponseEntity<? extends ArrayList<User>> responseEntity = restTemplate.getForEntity(restEndPointUrl, (Class<? extends ArrayList<User>>)ArrayList.class, userId);
Shravan Ramamurthy
sumber
4
Ini tidak akan berfungsi, karena karena penghapusan tidak ada informasi parameter tipe yang diteruskan ke getForEntity. Juga (Class<? extends ArrayList<User>>) ArrayList.classmemberikan kesalahan kompilasi "tipe yang tidak kompatibel".
Esko Luontola