Obtenir la liste des objets JSON avec Spring RestTemplate

199

J'ai deux questions:

  • Comment mapper une liste d'objets JSON à l'aide de Spring RestTemplate.
  • Comment mapper des objets JSON imbriqués.

J'essaie de consommer https://bitpay.com/api/rates , en suivant le tutoriel de http://spring.io/guides/gs/consuming-rest/ .

Karudi
la source
2
Pensez à voir cette réponse, surtout si vous souhaitez utiliser la liste des génériques stackoverflow.com/questions/36915823/…
Moesio

Réponses:

220

Peut-être de cette façon ...

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

Code de contrôleur pour le RequestMapping

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

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

ResponseEntityest une extension HttpEntityqui ajoute un HttpStatuscode d'état. Utilisé également dans RestTemplateles @Controllerméthodes. Dans RestTemplatecette classe est retourné par getForEntity()et exchange().

kamokaze
la source
Cela a fonctionné comme un charme, merci. Peut-être pouvez-vous me diriger vers d'autres tutoriels ou guides que je pourrais lire sur ce sujet?
Karudi
2
mieux de regarder ici sur stackoverflow pour quelques extraits de code et des exemples ou visitez le site Web de printemps officiel ...... TblGps [] a = responseEntity.getBody ();
kamokaze
Est-ce possible d'utiliser des génériques? c'est-à-dire que ma méthode a un paramètre Class <T extend Foo> et je voudrais obtenir une collection de T à partir de la méthode getForEntity.
Diskutant
Oui, cela devrait fonctionner, mais peut ne pas être prêt à l'emploi selon votre version printemps / jackson et vos types de classe. Il s'agit de génériques de sérialisation / désérialisation - la requête http ne se soucie pas de ce qui est transporté.
kamokaze
1
Voir également forum.spring.io/forum/spring-projects/android/… .
Benny Bottema
335

Définissez d'abord un objet pour contenir l'entité qui revient dans le tableau. Par exemple

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

Ensuite, vous pouvez consommer le service et obtenir une liste fortement typée via:

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

Les autres solutions ci-dessus fonctionneront également, mais j'aime récupérer une liste fortement typée au lieu d'un objet [].

Mat
la source
6
Cette course se déroule bien avec le printemps 4.2.3 et - comme Matt l'a dit - a le gros avantage d'éviter l'objet []
Margé
@Matt - quel marshaller utilisez-vous pour rassembler le json en objets Rate? Je suppose que c'est ce qui se passe ici, au moment où le restTemplate.exchangemarshallar mappe toutes les valeurs json aux noms de clé correspondants en tant que propriétés dans l'objet Rate. J'espère que mon processus de réflexion est correct.
Nirmal
Parfait, fonctionne très bien dans Spring Boot 1.4.0.RELEASE Merci
Anand
1
@Nirmal Spring utilise Jackson par défaut, je crois.
Sohaib
1
@SarvarNishonboev, la ParameterizedTypeReference actuelle de springframework.core semble toujours correcte: docs.spring.io/spring-framework/docs/current/javadoc-api/org/…
fspinnenhirn
75

Pour moi, cela a fonctionné

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

Où Object est la classe que vous voulez

yonia
la source
16
Cela fonctionne même si vous utilisez une classe et non un objet commeCoupon[] coupons = restTemplate.getForObject( url, Coupon[].class)
lrkwz
1
Cela peut provoquer NPE si le corps de réponse HTTP était vide (pas []mais totalement vide). Soyez donc prudent et vérifiez null( if (forNow != null)...).
Ruslan Stelmachenko
1
Saved my ass :) Vous vous demandez quel type est utilisé par Jackson, quand Object.classest spécifié dans la méthode getForObject().
Eric Wang
5

Après plusieurs tests, c'est le meilleur moyen que j'ai trouvé :)

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

Tout ce dont vous avez besoin

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
la source
Remarque: cela nécessite Guava
vphilipnyc
2

Mon gros problème ici était de construire la structure d'objet requise pour faire correspondre RestTemplate à une classe compatible. Heureusement, j'ai trouvé http://www.jsonschema2pojo.org/ (obtenez la réponse JSON dans un navigateur et utilisez-la comme entrée) et je ne peux pas le recommander assez!

en haut
la source
2

j'ai en fait développé quelque chose de fonctionnel pour l'un de mes projets avant et voici le code:

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

J'espère que cela aidera quelqu'un!

Hamza Jeljeli
la source
1

Si vous préférez une liste d'objets, une façon de le faire est la suivante:

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

Et utilisez-le comme ceci:

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

Des explications sur ce qui précède peuvent être trouvées ici ( https://www.baeldung.com/spring-rest-template-list ) et sont paraphrasées ci-dessous.

"Il y a quelques choses qui se passent dans le code ci-dessus. Premièrement, nous utilisons ResponseEntity comme type de retour, en l'utilisant pour envelopper la liste des objets que nous voulons vraiment. Deuxièmement, nous appelons RestTemplate.exchange () au lieu de getForObject () .

Il s'agit de la manière la plus générique d'utiliser RestTemplate. Il nous oblige à spécifier la méthode HTTP, le corps de requête facultatif et un type de réponse. Dans ce cas, nous utilisons une sous-classe anonyme de ParameterizedTypeReference pour le type de réponse.

Cette dernière partie est ce qui nous permet de convertir la réponse JSON en une liste d'objets de type approprié. Lorsque nous créons une sous-classe anonyme de ParameterizedTypeReference, elle utilise la réflexion pour capturer des informations sur le type de classe dans lequel nous voulons convertir notre réponse.

Il conserve ces informations à l'aide de l'objet Type de Java, et nous n'avons plus à nous soucier de l'effacement des types. "

Toofy
la source
1

Vous pouvez créer POJO pour chaque entrée comme,

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

puis en utilisant ParameterizedTypeReference of List of BitPay vous pouvez utiliser comme:

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
la source
-1

J'ai trouvé du travail autour de ce post https://jira.spring.io/browse/SPR-8263 .

Sur la base de ce message, vous pouvez renvoyer une liste dactylographiée comme ceci:

ResponseEntity<? extends ArrayList<User>> responseEntity = restTemplate.getForEntity(restEndPointUrl, (Class<? extends ArrayList<User>>)ArrayList.class, userId);
Shravan Ramamurthy
la source
4
Cela ne fonctionnera pas, car en raison de l'effacement, aucune information de paramètre de type n'est transmise à getForEntity. (Class<? extends ArrayList<User>>) ArrayList.classDonne également une erreur de compilation "types incompatibles".
Esko Luontola