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() + "×tamp=" + 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() + "×tamp=" + 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).
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.
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)
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>>() {})