在 Java Spring Boot 中使用 Rest Client 反序列化文本 json

问题描述 投票:0回答:2

我在处理来自 Spring 的外部 REST API 和 REST 客户端的响应时遇到问题。我目前正在使用新的(+3.2.0,如果我没记错的话)“Rest Client”,但我也会接受解决“Web Client”问题的答案,如果它适用并且您找不到新 Rest 客户端的答案。

简单地说,我向返回正文的外部 REST API 发出请求。内容类型标头内容为:

application/json; charset=utf-8

正文本身实际上并不是 Web 客户端或 Rest 客户端(我想)能够毫无问题地反序列化的普通 json。它似乎是一个文本 json,尽管该媒体类型本身并不存在。响应正文示例:

"{\"Code\":\"123-456\",\"Number\":1}"

让我重申,这是字面回应,而不是这个:

{
  "Code": "123-456",
  "Number": 1
}

我尝试过这个但没有成功:

{
  Object response = restClient.post()
    .uri(url)
    .contentType(MediaType.APPLICATION_JSON)
    .body(request)
    .retrieve()
    .body(ApiResponse.class);
}

我也尝试过:

{
  Object response = restClient.post()
    .uri(url)
    .contentType(MediaType.APPLICATION_JSON)
    .body(request)
    .retrieve()
    .body(String.class);
}

这两个都会抛出相同的错误,它们在

.body(...) part
处中断。我也尝试过
.toEntity(...)
,它也在那里破裂。我只展示第一个:

Error while extracting response for type [com.example.test.models.ApiResponse] and content type [application/json;charset=utf-8]

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.lang.String` from Object value (token `JsonToken.START_OBJECT`)
at [Source: (jdk.internal.net.http.ResponseSubscribers$HttpResponseInputStream); line: 1, column: 1]

我的猜测是,因为响应的内容类型标头是

application/json
,所以它需要一个以
{
开头的 json。然而,响应以
"
开头,因为就像我说的,它不是一个普通的 json,它是字符串文字格式的 json。因此
Cannot deserialize value of type 'java.lang.String' from Object value (token 'JsonToken.START_OBJECT')

我设法将其更改为“Rest Template”并执行其他操作,但我不想这样做。我想通过 Rest 客户端/Web 客户端来实现此功能。

请帮忙,我不知道还能做什么。另外,有人故意这样做有什么理由吗?我的意思是,使响应成为 json 字符串而不是普通的 json。如果这个外部 REST API 以正常的 json 响应(我猜),我现在就不会写这个。

编辑:我使用默认的 Rest 客户端和默认的消息转换器。

编辑2:我调查了一下,我知道是什么引发了这个错误。我将把它留在下面的答案中。然而,仍然存在一个问题,与问题的标题更加一致。我也会把它留在那里。

编辑3:我在另一个问题中关注这个:Deserializing Json from String with Rest Client and Object Mapper

java json spring rest client
2个回答
0
投票

我还没能尝试解决方案,但它可能会给你一些想法:

您尝试过使用

bodyToMono(String.class)
来代替吗?即使一般不推荐,您也可以通过
.block()
关注它。

然后使用 jackson 将你的responseString转换为json:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import reactor.core.publisher.Mono;

// ...

String responseString = restClient.post()
    .uri(url)
    .contentType(MediaType.APPLICATION_JSON)
    .body(request)
    .retrieve()
    .bodyToMono(String.class)
    .block();

ObjectMapper objectMapper = new ObjectMapper();
JsonNode responseJson = objectMapper.readTree(responseString);

0
投票

因此,这个外部 API 返回的是 500 而不是 200。这触发了我拥有的错误处理程序,但我不知道。问题是我能够在错误处理程序中检查错误消息。这样,我就能够解决第一个问题了。

事实证明,这个外部 API 期望请求正文是一个 string,而不是 object

预计是这样的:

String responseString = restClient.post()
    .uri(url)
    .contentType(MediaType.APPLICATION_JSON)
    .body(request.toJsonString()) // <-- method to convert it to a json string that I created
//...

不是这个:

String responseString = restClient.post()
    .uri(url)
    .contentType(MediaType.APPLICATION_JSON)
    .body(request) // <-- original object from ApiRequest class
//...

所以,是的,很抱歉浪费了你的时间,该死的这个API,文档很少,而且有一些有问题的设计选择。

但是现在我有另一个问题,而且它与标题问题更相关。现在使用这段代码:

Object response = restClient.post()
    .uri(url)
    .contentType(MediaType.APPLICATION_JSON)
    .body(request.toJsonString())
    .retrieve()
    .body(ApiResponse.class);

.body(...)
部分我遇到以下异常:

org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.example.test.models.ApiResponse` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"Code":"123-456","Number":1}')

ApiResponse 类如下所示:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse {
  @JsonProperty("Code")
  private String code;
  @JsonProperty("Number")
  private String number;
}

外部 API 响应的内容是字面意思(根据 Postman 的说法):

"{\"Code\":\"123-456\",\"Number\":1}"

我错过了什么吗?

© www.soinside.com 2019 - 2024. All rights reserved.