I have a problem during the deserialization of a json array using Spring. I have this json response from a 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 }, .... ]
I get this json using the new WebClient from Spring WebFlux, here the 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 is a simple pojo that maps the fields.
Actually, when I hit a get it says:
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]
How can I deserialize the json properly using the new webflux module? What am I doing wrong?
UPDATE 05/02/2018
Both answers are correct. They addressed perfectly my question but at the end I decided to use a slightly different approach:
@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(); }); }
An alternative to this could be to return directly A Flux so you don't have to convert it to a list. (that's what flux are: a collection of n elements).
WebClient client = WebClient. of(); // Send a serialized JSON object with "applicaiton/json" HttpResponse response = client. postJson("/items", new MyItem()); HttpResponse response = client. putJson("/items", new MyItem()); HttpResponse response = client.
You would just use bodyToMono which would return a Mono object. WebClient webClient = WebClient. create(); String responseJson = webClient. get() .
Mono is a reactive publisher, that can emit 0 zero or 1 elements. Thus, in order to retrieve a single JSON resource with WebClient, we should use Mono publisher. We will use WebClient to read a JSON object and parse it into POJO. Example of WebClient reading single JSON Object as a POJO with Mono.
Regarding your updated answer to your question, using bodyToFlux
is unnecessarily inefficient and semantically doesn't make much sense either as you don't really want a stream of orders. What you want is simply to be able to parse the response as a list.
bodyToMono(List<AccountOrder>.class)
won't work due to type erasure. You need to be able to retain the type at runtime, and Spring provides ParameterizedTypeReference
for that:
bodyToMono(new ParameterizedTypeReference<List<AccountOrder>>() {})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With