web-dev-qa-db-ja.com

JacksonとWebClientを使用してjson配列をオブジェクトに逆シリアル化する

Springを使用したjson配列のデシリアライズ中に問題が発生しました。サービスからこのjson応答があります。

[
    {
        "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
    },
    ....
]

Spring WebFluxの新しいWebClientを使用してこのjsonを取得します。ここではコードを使用します。

@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は、フィールドをマップする単純なpojoです。

実際、私がgetを押すと、それは言う:

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]

新しいwebfluxモジュールを使用してjsonを適切にデシリアライズするにはどうすればよいですか?何が悪いのですか?

2018年5月2日更新

どちらも正解です。彼らは私の質問に完全に対処しましたが、最終的には少し異なるアプローチを使用することにしました。

@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();
        });
    }

これに代わる方法として、A Fluxを直接返すことで、リストに変換する必要がなくなります。 (それがフラックスです:n要素のコレクション)。

11
Justin

レスポンスをAccountOrderListクラスと一致させるには、jsonは次のようにする必要があります

{
  "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
    },
    ....
]
}

これは、エラーメッセージに "START_ARRAYトークンが不足しています"と表示されています。

応答を変更できない場合は、このように配列を受け入れるようにコードを変更してください

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

この配列をリストに変換してから返すことができます。

9
pvpkiran

あなたの応答は単にList<AccountOrder>。しかし、POJOはラップしましたList<AccountOrder>。したがって、POJOによれば、JSON

{
  "accountOrders": [
    {

しかし、あなたはJSONです

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

したがって、ミスマッチがあり、逆シリアル化に失敗します。に変更する必要があります

bodyToMono(AccountOrder[].class)
4
Ravi

質問への更新された回答に関して、bodyToFluxを使用することは不必要に非効率的であり、実際には注文の流れを必要としないため、意味的にもあまり意味がありません。必要なのは、単に応答をリストとして解析できるようにすることです。

bodyToMono(List<AccountOrder>.class)は型の消去のため機能しません。実行時に型を保持できる必要があり、SpringはそのためにParameterizedTypeReferenceを提供します。

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

3
Pin