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
.