Home > OS >  Java Spring Boot response not converting whole response to JSON
Java Spring Boot response not converting whole response to JSON

Time:01-19

I am having an issue where my Spring Boot response Entity is mostly being converted to JSON but the body is staying as a string.

I have read many of the other questions on this site, but they all seem to point to the same thing.

pom.xml Dependencies

    <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 Class

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;
    }

}

processResponse method

    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 method

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

The response

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

The problem is with "body": "{\"status\":\"Healthy!\"}"

I have been unsuccessful in getting the body to be converted from a string to JSON when returned through my Spring Boot ResponseEntity.

CodePudding user response:

You are getting exactly what you asked for with the definition of ApiResponse.

public String body;

If you want Jackson to serialize that as a part of the object. It needs to be defined as part of the object. This may be as simple as changing it to JsonNode.

public JsonNode body;

And generating a JsonNode from the entity.

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

Although it would probably be better if your ApiResponse object more accurately modeled what it is holding.

CodePudding user response:

As others have said, by declaring the type of ApiResponse.body as String, that's exactly what's being stored there; the JSON serializer doesn't have any knowledge of the content of the String, so it just serializes it out (with required escaping to make it a valid string value).

I can think of 2 options to make that a more useful object that can be serialized: A) transform the response HttpEntity you get from the downstream API into a simple DTO with just the properties you care about; or B) use HttpEntity<> directly and instruct the serializer to ignore properties you don't care about (such as the EofSensorInputStream from the exception you reported).

Option A seems fairly obvious so I won't show code for that. Here's how you can accomplish Option B:

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

    // ... other methods ...

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

That instructs the serializer to only include body and headers when it serializes the body of ApiResponse, ignoring anything else in the HttpEntity.

  • Related