web-dev-qa-db-fra.com

Désérialiser un tableau json en objets à l'aide de Jackson et WebClient

J'ai un problème lors de la désérialisation d'un tableau json à l'aide de Spring. J'ai cette réponse json d'un service:

[
    {
        "symbol": "XRPETH",
        "orderId": 12122,
        "clientOrderId": "xxx",
        "price": "0.00000000",
        "origQty": "25.00000000",
        "executedQty": "25.00000000",
        "status": "FILLED",
        "timeInForce": "GTC",
        "type": "MARKET",
        "side": "BUY",
        "stopPrice": "0.00000000",
        "icebergQty": "0.00000000",
        "time": 1514558190255,
        "isWorking": true
    },
    {
        "symbol": "XRPETH",
        "orderId": 1212,
        "clientOrderId": "xxx",
        "price": "0.00280000",
        "origQty": "24.00000000",
        "executedQty": "24.00000000",
        "status": "FILLED",
        "timeInForce": "GTC",
        "type": "LIMIT",
        "side": "SELL",
        "stopPrice": "0.00000000",
        "icebergQty": "0.00000000",
        "time": 1514640491287,
        "isWorking": true
    },
    ....
]

J'obtiens ce json en utilisant le nouveau WebClient de Spring WebFlux, voici le code:

@Override
    public Mono<AccountOrderList> getAccountOrders(String symbol) {
        return binanceServerTimeApi.getServerTime().flatMap(serverTime -> {
            String apiEndpoint = "/api/v3/allOrders?";
            String queryParams = "symbol=" +symbol.toUpperCase() + "&timestamp=" + serverTime.getServerTime();
            String signature = HmacSHA256Signer.sign(queryParams, secret);
            String payload = apiEndpoint + queryParams + "&signature="+signature;
            log.info("final endpoint:"+ payload);
            return this.webClient
                    .get()
                    .uri(payload)
                    .accept(MediaType.APPLICATION_JSON)
                    .retrieve()
                    .bodyToMono(AccountOrderList.class)
                    .log();
        });
    }

AccountOrderList

public class AccountOrderList {

    private List<AccountOrder> accountOrders;

    public AccountOrderList() {
    }

    public AccountOrderList(List<AccountOrder> accountOrders) {
        this.accountOrders = accountOrders;
    }

    public List<AccountOrder> getAccountOrders() {
        return accountOrders;
    }

    public void setAccountOrders(List<AccountOrder> accountOrders) {
        this.accountOrders = accountOrders;
    }
}

AccountOrder est un simple pojo qui mappe les champs.

En fait, quand je frappe un get, il dit:

org.springframework.core.codec.DecodingException: JSON decoding error: Cannot deserialize instance of `io.justin.demoreactive.domain.AccountOrder` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `io.justin.demoreactive.domain.AccountOrder` out of START_ARRAY token
 at [Source: UNKNOWN; line: -1, column: -1]

Comment désérialiser correctement le json en utilisant le nouveau module webflux? Qu'est-ce que je fais mal?

MISE À JOUR 05/02/2018

Les deux réponses sont correctes. Ils ont parfaitement répondu à ma question mais à la fin j'ai décidé d'utiliser une approche légèrement différente:

@Override
    public Mono<List<AccountOrder>> getAccountOrders(String symbol) {
        return binanceServerTimeApi.getServerTime().flatMap(serverTime -> {
            String apiEndpoint = "/api/v3/allOrders?";
            String queryParams = "symbol=" +symbol.toUpperCase() + "&timestamp=" + serverTime.getServerTime();
            String signature = HmacSHA256Signer.sign(queryParams, secret);
            String payload = apiEndpoint + queryParams + "&signature="+signature;
            log.info("final endpoint:"+ payload);
            return this.webClient
                    .get()
                    .uri(payload)
                    .accept(MediaType.APPLICATION_JSON)
                    .retrieve()
                    .bodyToFlux(AccountOrder.class)
                    .collectList()
                    .log();
        });
    }

Une alternative à cela pourrait être de retourner directement A Flux afin que vous n'ayez pas à le convertir en liste. (c'est ce que sont les flux: une collection de n éléments).

11
Justin

Pour que la réponse soit mise en correspondance avec la classe AccountOrderList, json doit être comme ceci

{
  "accountOrders": [
    {
        "symbol": "XRPETH",
        "orderId": 12122,
        "clientOrderId": "xxx",
        "price": "0.00000000",
        "origQty": "25.00000000",
        "executedQty": "25.00000000",
        "status": "FILLED",
        "timeInForce": "GTC",
        "type": "MARKET",
        "side": "BUY",
        "stopPrice": "0.00000000",
        "icebergQty": "0.00000000",
        "time": 1514558190255,
        "isWorking": true
    },
    {
        "symbol": "XRPETH",
        "orderId": 1212,
        "clientOrderId": "xxx",
        "price": "0.00280000",
        "origQty": "24.00000000",
        "executedQty": "24.00000000",
        "status": "FILLED",
        "timeInForce": "GTC",
        "type": "LIMIT",
        "side": "SELL",
        "stopPrice": "0.00000000",
        "icebergQty": "0.00000000",
        "time": 1514640491287,
        "isWorking": true
    },
    ....
]
}

Voici ce que dit le message d'erreur "sur le jeton START_ARRAY"

Si vous ne pouvez pas modifier la réponse, modifiez votre code pour accepter un tableau comme celui-ci

this.webClient.get().uri(payload).accept(MediaType.APPLICATION_JSON)
                        .retrieve().bodyToMono(AccountOrder[].class).log();

Vous pouvez convertir ce tableau en liste, puis revenir.

9
pvpkiran

Votre réponse est simplement List<AccountOrder>. Mais, votre POJO a enveloppé List<AccountOrder>. Donc, selon votre POJO, votre JSON devrait être

{
  "accountOrders": [
    {

Mais vous êtes JSON est

[
    {
       "symbol": "XRPETH",
       "orderId": 12122,
        ....

Il y a donc un décalage et un échec de la désérialisation. Vous devez changer pour

bodyToMono(AccountOrder[].class)
4
Ravi

En ce qui concerne votre réponse mise à jour à votre question, l'utilisation de bodyToFlux est inutilement inefficace et n'a pas beaucoup de sens sémantique car vous ne voulez pas vraiment un flux de commandes. Ce que vous voulez, c'est simplement pouvoir analyser la réponse sous forme de liste.

bodyToMono(List<AccountOrder>.class) ne fonctionnera pas en raison de l'effacement du type. Vous devez pouvoir conserver le type lors de l'exécution, et Spring fournit ParameterizedTypeReference pour cela:

bodyToMono(new ParameterizedTypeReference<List<AccountOrder>>() {})

3
Pin