When I try to get a list of values using RestTemplate, I get an error. I use Spring 3.0.0.
Caused by: org.springframework.web.client.RestClientException: Error while extracting response for type [class [Lru.sandwichcloud.Ingredient;] and content type [application/json] at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:118) ~[spring-web-6.0.2.jar!/:6.0.2] at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1132) ~[spring-web-6.0.2.jar!/:6.0.2] at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1115) ~[spring-web-6.0.2.jar!/:6.0.2] at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:865) ~[spring-web-6.0.2.jar!/:6.0.2] at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:764) ~[spring-web-6.0.2.jar!/:6.0.2] at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:405) ~[spring-web-6.0.2.jar!/:6.0.2] at ru.sandwichcloud.SandwichCloudClient.getAllIngredients(SandwichCloudClient.java:42) ~[classes!/:0.0.7-SNAPSHOT] at ru.sandwichcloud.RestExamples.lambda$fetchIngredients$0(RestExamples.java:34) ~[classes!/:0.0.7-SNAPSHOT] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:767) ~[spring-boot-3.0.0.jar!/:3.0.0] 11 common frames omitted Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type
[Lru.sandwichcloud.Ingredient;
from Object value (tokenJsonToken.START_OBJECT
) at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:406) ~[spring-web-6.0.2.jar!/:6.0.2] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:354) ~[spring-web-6.0.2.jar!/:6.0.2] at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:103) ~[spring-web-6.0.2.jar!/:6.0.2] 19 common frames omitted Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type[Lru.sandwichcloud.Ingredient;
from Object value (tokenJsonToken.START_OBJECT
) at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 1] at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59) ~[jackson-databind-2.14.1.jar!/:2.14.1] at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1746) ~[jackson-databind-2.14.1.jar!/:2.14.1] at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1520) ~[jackson-databind-2.14.1.jar!/:2.14.1] at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1467) ~[jackson-databind-2.14.1.jar!/:2.14.1] at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.handleNonArray(ObjectArrayDeserializer.java:345) ~[jackson-databind-2.14.1.jar!/:2.14.1] at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:196) ~[jackson-databind-2.14.1.jar!/:2.14.1] at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:26) ~[jackson-databind-2.14.1.jar!/:2.14.1] at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323) ~[jackson-databind-2.14.1.jar!/:2.14.1] at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2105) ~[jackson-databind-2.14.1.jar!/:2.14.1] at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1481) ~[jackson-databind-2.14.1.jar!/:2.14.1] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:395) ~[spring-web-6.0.2.jar!/:6.0.2] 21 common frames omitted
This is how JSON object looks like for "http://localhost:8080/data-api/ingredients":
"_embedded" : {
"ingredients" : [ {
"name" : "Dark Bread",
"type" : "BREAD",
"_links" : {
"self" : {
"href" : "http://localhost:8080/data-api/ingredients/DARK"
},
"ingredient" : {
"href" : "http://localhost:8080/data-api/ingredients/DARK"
}
}
}, {
"name" : "White Bread",
"type" : "BREAD",
"_links" : {
"self" : {
"href" : "http://localhost:8080/data-api/ingredients/WHTE"
},
"ingredient" : {
"href" : "http://localhost:8080/data-api/ingredients/WHTE"
}
}
}, {
"name" : "Beef",
"type" : "PROTEIN",
"_links" : {
"self" : {
"href" : "http://localhost:8080/data-api/ingredients/BEEF"
},
"ingredient" : {
"href" : "http://localhost:8080/data-api/ingredients/BEEF"
}
}
}, {
"name" : "Mustard",
"type" : "SAUCE",
"_links" : {
"self" : {
"href" : "http://localhost:8080/data-api/ingredients/MTRD"
},
"ingredient" : {
"href" : "http://localhost:8080/data-api/ingredients/MTRD"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/data-api/ingredients"
},
"profile" : {
"href" : "http://localhost:8080/data-api/profile/ingredients"
}
}
}
How I try to get an object:
public List<Ingredient> getAllIngredients() {
return rest.exchange("http://localhost:8080/data-api/ingredients",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<Ingredient>>() {}
).getBody();
}
Ingredient:
@Data
@RequiredArgsConstructor
@NoArgsConstructor(access=AccessLevel.PRIVATE, force=true)
@Entity
public class Ingredient {
@Id
private final String id;
private final String name;
private final Type type;
public enum Type {
BREAD, PROTEIN, VEGGIES, CHEESE, SAUCE
}
}
When I get only one object by Id getIngredientById("BEEF")
, it works fine. What am I doing wrong?
public Ingredient getIngredientById(String ingredientId) {
return rest.getForObject(
"http://localhost:8080/data-api/ingredients/{id}",
Ingredient.class,
ingredientId);
}
JSON from "http://localhost:8080/data-api/ingredients/BEEF"
{
"name" : "Beef",
"type" : "PROTEIN",
"_links" : {
"self" : {
"href" : "http://localhost:8080/data-api/ingredients/BEEF"
},
"ingredient" : {
"href" : "http://localhost:8080/data-api/ingredients/BEEF"
}
}
}
CodePudding user response:
The one problem I can see when the list is being returned it has "_embedded" : { }
element as root, so when you deserialize the response to List<Ingradient>
it will not work. For single object it is working because you have response with Ingredient {}
element is at the root. Either you need to get rid of _embadded
from the API response or you have your object modified to incorporate exact structure you received from "http://localhost:8080/data-api/ingredients"
API
CodePudding user response:
Understanding exchang method
When you passing ParameterizedTypeReference<?> as response, it returns ResponseEntity
Instead of
public List<Ingredient> getAllIngredients() {
return rest.exchange("http://localhost:8080/data-api/ingredients",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<Ingredient>>() {}
).getBody();
}
Try this using ResponseEntity
public List<Ingredient> getAllIngredients() {
ResponseEntity<List<Ingredient>> result = restTemplate.exchange("http://localhost:8080/data-api/ingredients", HttpMethod.GET, null, new ParameterizedTypeReference<List<Ingredient>>() {});
List<Ingredient> list = result.getBody();
return list;
}