java 使用 Jackson 和 WebClient 将 json 数组反序列化为对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/48598233/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 10:07:07  来源:igfitidea点击:

Deserialize a json array to objects using Hymanson and WebClient

javaspringspring-bootHymansonspring-webflux

提问by Justin

I have a problem during the deserialization of a json array using Spring. I have this json response from a service:

我在使用 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
    },
    ....
]

I get this json using the new WebClient from Spring WebFlux, here the code:

我使用来自 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 is a simple pojo that maps the fields.

AccountOrder 是一个映射字段的简单 pojo。

Actually, when I hit a get it says:

实际上,当我点击 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.Hymanson.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?

如何使用新的 webflux 模块正确反序列化 json?我究竟做错了什么?

UPDATE 05/02/2018

更新 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() + "&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();
        });
    }

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).

另一种方法是直接返回 A Flux ,因此您不必将其转换为列表。(这就是通量:n 个元素的集合)。

采纳答案by pvpkiran

For the response to be matched with AccountOrderListclass, json has to be like this

响应要与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
    },
    ....
]
}

This is what the error message says "out of START_ARRAY token"

这就是错误消息所说的“超出 START_ARRAY 令牌

If you cannot change the response, then change your code to accept Array like this

如果您无法更改响应,请更改您的代码以接受这样的数组

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

You can convert this array to List and then return.

您可以将此数组转换为 List,然后返回。

回答by Pin

Regarding your updated answer to your question, using bodyToFluxis 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.

关于您对问题的更新答案,使用bodyToFlux不必要的低效并且在语义上也没有多大意义,因为您并不真正想要订单流。您想要的只是能够将响应解析为列表。

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 ParameterizedTypeReferencefor that:

bodyToMono(List<AccountOrder>.class)由于类型擦除而无法工作。您需要能够在运行时保留类型,Spring 提供ParameterizedTypeReference了这一点:

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

回答by Ravi

Your response is simply List<AccountOrder>. But, your POJO has wrapped List<AccountOrder>. So, according to your POJO, your JSONshould be

您的回答很简单List<AccountOrder>。但是,您的 POJO 已将List<AccountOrder>. 所以,根据你的 POJO,你JSON应该是

{
  "accountOrders": [
    {

But, you are JSONis

但是,你JSON

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

So, there is mismatch and failing the deserialization. You need to change to

因此,存在不匹配和反序列化失败。您需要更改为

bodyToMono(AccountOrder[].class)