skip to Main Content

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.

2

Answers


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

    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search