【问题标题】:Java Spring Boot response not converting whole response to JSONJava Spring Boot 响应没有将整个响应转换为 JSON
【发布时间】:2023-02-15 00:58:35
【问题描述】:

我遇到一个问题,我的 Spring Boot 响应实体大部分被转换为 JSON,但正文仍保留为字符串。

我已经阅读了该站点上的许多其他问题,但它们似乎都指向同一件事。

pom.xml 依赖项

    <dependencies>
        <!--OpenAPI Generator Dependencies-->
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.0.2</version>
        </dependency>
        <!--Http Dependencies-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <!--Log4j Dependencies-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <!--Spring Framework Dependencies-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--Dotenv Dependencies-->
        <dependency>
            <groupId>io.github.cdimascio</groupId>
            <artifactId>java-dotenv</artifactId>
            <version>${dotenv.version}</version>
        </dependency>

        <!--JSON Object Dependencies-->
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20220924</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.14.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.14.1</version>
        </dependency>

    </dependencies>

ApiResponse 类

public class ApiResponse {
    // class used to model api response data
    public Integer statusCode;
    public String statusReason;
    public String statusLine;
    public String url;
    public String body;
    public ProtocolVersion protocolVersion;

    public String getStatusLine() {
        return statusLine;
    }

    public void setStatusLine(String statusLine) {
        this.statusLine = statusLine;
    }

    public ProtocolVersion getProtocolVersion() {
        return protocolVersion;
    }

    public String getStatusReason() {
        return statusReason;
    }

    public void setStatusReason(String statusReason) {
        this.statusReason = statusReason;
    }

    public void setProtocolVersion(ProtocolVersion protocolVersion) {
        this.protocolVersion = protocolVersion;
    }

    public Integer getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(Integer statusCode) {
        this.statusCode = statusCode;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

}

过程响应方法

    public ApiResponse processResponse(CloseableHttpResponse response) throws IOException {
        // process custom apache httpClient response
        ApiResponse apiResponse = new ApiResponse();
        apiResponse.setProtocolVersion(response.getProtocolVersion());
        apiResponse.setStatusCode(response.getStatusLine().getStatusCode());
        apiResponse.setStatusReason(response.getStatusLine().getReasonPhrase());
        apiResponse.setStatusLine(response.getStatusLine().toString());

        HttpEntity entity = response.getEntity();
        if (entity != null) {
            // return it as a String
            String result = EntityUtils.toString(entity);
            apiResponse.setBody(result);
        }
        response.close();
        return apiResponse;
    }

getCall 方法

    @GetMapping(value = "/getHealth", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<ApiResponse> getClientHealth() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {

        Client clientSetup = new Client(dotEnv.get("URL"), 80);

        try {
            ApiResponse response = clientSetup.getHealth();

            return new ResponseEntity<>(response, HttpStatusCode.valueOf(response.getStatusCode()));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

响应

{
  "statusCode": 200,
  "statusReason": "OK",
  "statusLine": "HTTP/1.1 200 OK",
  "url": null,
  "body": "{\"status\":\"Healthy!\"}",
  "protocolVersion": {
    "protocol": "HTTP",
    "major": 1,
    "minor": 1
  }
}

问题出在"body": "{\"status\":\"Healthy!\"}"

当通过我的 Spring Boot ResponseEntity 返回时,我未能成功将正文从字符串转换为 JSON。

【问题讨论】:

  • body成员连载前长啥样?看起来您正在将 JSON 放入 JSON 中。
  • @stdunbar 如果我在 ResponseEntity 之前打印到标准输出,我会得到 {"status":"Healthy!"}
  • 为什么你想要身体作为字符串?保持类型为HttpEntity并删除这个EntityUtils.toString(entity)。它应该工作正常
  • @pvpkiran 如果我在我的 Response 类中将类型更改为 HttpEntity 并传递该实体,我会收到一个错误。 No serializer found for class org.apache.http.conn.EofSensorInputStream and no properties discovered to create BeanSerializer

标签: java json spring spring-boot jackson-databind


【解决方案1】:

通过 ApiResponse 的定义,您得到的正是您所要求的。

public String body;

如果您希望 Jackson 将其序列化为对象的一部分。它需要被定义为对象的一部分。 这可能就像将其更改为 JsonNode 一样简单。

public JsonNode body;

并从实体生成 JsonNode

if (entity != null) {
  // return it as a JsonNode
  String stringResponse = EntityUtils.toString(entity);
  ObjectMapper mapper = new ObjectMapper();
  JsonNode jsonNode = mapper.readTree(stringResponse);
  apiResponse.setBody(jsonNode);
}

尽管如果您的 ApiResponse 对象更准确地模拟它所持有的内容可能会更好。

【讨论】:

  • 感谢你的回答。我想我误解了 Jackson 使用 Spring Boot ResponseEntity 进行的转换。如果我按照您的建议进行更改,那么我会收到 Jackson 的错误消息。 com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.apache.http.conn.EofSensorInputStream and no properties discovered to create BeanSerializer
  • 这是最接近的答案。我仍然需要将实体转换为字符串。然后,使用 ObjectMapper mapper = new ObjectMapper(); 将字符串读取到 JsonNode --> `JsonNode jsonNode = mapper.readTree(string);
  • 如果您要执行此操作(我发现这样做不必要地冗长),请确保注入 ObjectMapper 而不是实例化它。如果您不注入它,则嵌套主体和 ApiResponse 的序列化方式之间的 JSON 输出可能会有所不同。我从事的大多数 Spring 项目都会对序列化进行一些自定义,这仅适用于在 Spring 中配置的 ObjectMapper。
  • @E-Riz,我同意每一点。我最初使用 .convert 调用来获取 JsonNode。但是,@ryan-burch 在使用该解决方案时遇到了一些麻烦,而这是对他们有用的版本。
【解决方案2】:

正如其他人所说,通过将 ApiResponse.body 的类型声明为字符串,这正是存储在那里的内容; JSON 序列化程序不知道字符串的内容,因此它只是将其序列化(需要转义以使其成为有效的字符串值)。

我可以想到 2 个选项来使它成为一个更有用的可以序列化的对象:A)将从下游 API 获得的响应 HttpEntity 转换为仅包含您关心的属性的简单 DTO;或 B) 直接使用 HttpEntity&lt;&gt; 并指示序列化程序忽略您不关心的属性(例如您报告的异常中的 EofSensorInputStream)。

选项 A 似乎很明显,所以我不会显示它的代码。以下是完成选项 B 的方法:

public class ApiResponse {
    // ... other fields ...
    private HttpEntity<?> body;

    // ... other methods ...

    @JsonIncludeProperties({"body", "headers"})
    public HttpEntity<?> getBody() {
        return body;
    }

这指示序列化程序在序列化 ApiResponsebody 时仅包含 bodyheaders,而忽略 HttpEntity 中的任何其他内容。

【讨论】:

  • 我无法让它工作。返回始终是一个空的 JSON 对象。所以它看起来像这样body: {}
  • 真奇怪;我实际上对此进行了编码并亲自看到了结果。我想知道你做了什么不同的......
  • 你考虑过我的选项A吗?这确实是最稳健的方式,尽管有点模式代码。
  • 可能是因为我同时使用了 apache http 库和 springboot 库。所以,我认为 HttpEntity 是从 apache 库中导入的。这将是不同的。选项 A 是一个可靠的选项,我会考虑。
猜你喜欢
  • 2019-05-17
  • 2018-06-02
  • 1970-01-01
  • 2018-02-13
  • 1970-01-01
  • 2016-02-25
  • 2021-11-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多