如何从响应式 WebClient 响应中获取数据

How to get data from reactive WebClient response

我想用响应式 WebClient 解析来自 stackexchange 的一些数据,但它抛出异常:

2021-02-03 23:55:17.544  INFO 16180 --- [           main] by.dzikovskiy.idt.Application            : Starting Application using Java 11.0.9.1 on DESKTOP-MT9VJGK with PID 16180 (C:\projects\WebCient\target\classes started by Vitaliy in C:\projects\WebCient)
2021-02-03 23:55:17.545  INFO 16180 --- [           main] by.dzikovskiy.idt.Application            : No active profile set, falling back to default profiles: default
2021-02-03 23:55:18.438  INFO 16180 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port 8081
2021-02-03 23:55:18.446  INFO 16180 --- [           main] by.dzikovskiy.idt.Application            : Started Application in 1.149 seconds (JVM running for 1.772)
Exception in thread "main" org.springframework.core.codec.DecodingException: JSON decoding error: Cannot deserialize instance of `by.dzikovskiy.idt.entity.ItemsData` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `by.dzikovskiy.idt.entity.ItemsData` out of START_ARRAY token
 at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 10] (through reference chain: by.dzikovskiy.idt.entity.StackResponse["items"])
    at org.springframework.http.codec.json.AbstractJackson2Decoder.processException(AbstractJackson2Decoder.java:228)
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ Body from GET https://api.stackexchange.com/2.2/answers?site=Whosebug&page=1&pagesize=5&order=desc&sort=activity&filter=default [DefaultClientResponse]

我有这样的实体类

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class StackResponse {
    private ItemsData items;
}

@JsonIgnoreProperties(ignoreUnknown = true)
@ToString
public class ItemsData {
    @JacksonXmlElementWrapper(useWrapping = false)
    List<Owner> owner;

    @JacksonXmlElementWrapper(useWrapping = false)
    public List<Owner> getOwner() {
        return owner;
    }

    public void setOwner(List<Owner> owner) {
        this.owner = owner;
    }
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonTypeName("owner")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
public class Owner {
    @JsonProperty("user_id")
    private long userId;
    @JsonProperty("display_name")
    private String displayName;
    private String link;

}

发出请求的WebClient

@Component
public class StackResponseClient {

    private final WebClient webClient;

    @Autowired
    public StackResponseClient(WebClient webClient) {
        this.webClient = webClient;
    }

    public Mono<StackResponse> getOwners() {
        return this.webClient
                .get()
                .uri("https://api.stackexchange.com/2.2/answers?site=Whosebug&page=1&pagesize=5&order=desc&sort=activity&filter=default")
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(StackResponse.class);
    }

}

和主要

@SpringBootApplication
public class Application {


    public static void main(String[] args) throws JsonProcessingException {
        SpringApplication.run(Application.class, args);

        ApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class);

        // code for testing json mapper
//         ObjectMapper objectMapper = new ObjectMapper();
//        StackResponse stackResponse = new StackResponse();
//        ItemsData itemsData = new ItemsData();
//        itemsData.setOwner(List.of(
//                new Owner(1,"one","link1"),
//                new Owner(2,"two","link2"),
//                new Owner(3,"three","link3")
//                ));
//        stackResponse.setItems(itemsData);
//        System.out.println(objectMapper.writeValueAsString(stackResponse));

        StackResponseClient stackResponseClient = ctx.getBean(StackResponseClient.class);

        StackResponse stackResponse = stackResponseClient.getOwners().block();
        System.out.println(stackResponse);
    }

}

我还测试了 jackson ser/deserialize 我的 类 和它的 json 结构与来自 stackexchange 的响应有何不同。也许这就是问题所在。

{
    "items": {
        "owner": [
            {
                "owner": {
                    "link": "link1",
                    "user_id": 1,
                    "display_name": "one"
                }
            },
            {
                "owner": {
                    "link": "link2",
                    "user_id": 2,
                    "display_name": "two"
                }
            },
            {
                "owner": {
                    "link": "link3",
                    "user_id": 3,
                    "display_name": "three"
                }
            }
        ]
    }
}

问题

我需要更改什么才能在 main 中获得 StackResponse 对象或至少像 List<Owner> 这样的列表?

谢谢

我相信你想要反序列化的响应具有这种结构

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class Response {
    private List<Item> items;
}

Items.class

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class Item {
    private Owner owner;
}

Owner.class

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class Owner {
    private int reputation;
    private int user_id;
    private String link;
    private String display_name;
    
    // etc. etc.

}