I am using spring-data-rest to expose REST API. One of my search method returns list of entities. The repository and rest response as follows
List<Order> findByKeywordContaining(String keyword);
Search Response:
{
"_embedded":{
"orders":[
{
"keyword":"Iron mattress",
"name":"Hostel",
"_links":{
"self":{
"href":"http://localhost:8081/orders/2"
},
"order":{
"href":"http://localhost:8081/orders/2"
}
}
},
{
"keyword":"Iron",
"name":"Weat strong",
"_links":{
"self":{
"href":"http://localhost:8081/orders/40"
},
"order":{
"href":"http://localhost:8081/orders/40"
}
}
}
]
},
"_links":{
"self":{
"href":"http://localhost:8081/orders/search/findByKeywordContaining?keyword=iron"
}
}
}
Now I am using RestTemplate to consume this response to process in client side as follows
List<Order> orders = restOperations.exchange(new URI(url), HttpMethod.GET, null, new ParameterizedTypeReference<Resource<List<Order>>>() {}).getBody().getContent();
The above code works fine for a single object in response, but if the response contains multiple objects. The code throws the following error
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Unrecognized field "_embedded" (class org.springframework.hateoas.Resource), not marked as ignorable (3 known properties: , "links", "content", "page"])
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@6a2bc870; line: 2, column: 18] (through reference chain: org.springframework.hateoas.Resource["_embedded"]); nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "_embedded" (class org.springframework.hateoas.Resource), not marked as ignorable (3 known properties: , "links", "content", "page"])
What I am missing here? How to consume the above response as a List?
Order.java
public class Order {
private int id;
private String name;
private String keyword;
private Agent agent;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public Agent getAgent() {
return agent;
}
public void setAgent(Agent agent) {
this.agent = agent;
}
Full Error Stack
21:39:30.746 [http-bio-8080-exec-36] DEBUG c.o.x.o.accessor.XWorkMethodAccessor - Error calling method through OGNL: object: [com.ams.order.actions.ViewOrderSearchAction@5218c8df] method: [viewOrderSearch] args: [[]]
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Unrecognized field "_embedded" (class org.springframework.hateoas.Resource), not marked as ignorable (2 known properties: , "links", "content"])
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@7583f177; line: 2, column: 18] (through reference chain: org.springframework.hateoas.Resource["_embedded"]); nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "_embedded" (class org.springframework.hateoas.Resource), not marked as ignorable (2 known properties: , "links", "content"])
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@7583f177; line: 2, column: 18] (through reference chain: org.springframework.hateoas.Resource["_embedded"])
at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readJavaType(MappingJackson2HttpMessageConverter.java:181) ~[spring-web-3.2.17.RELEASE.jar:3.2.17.RELEASE]
at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.read(MappingJackson2HttpMessageConverter.java:173) ~[spring-web-3.2.17.RELEASE.jar:3.2.17.RELEASE]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:94) ~[spring-web-3.2.17.RELEASE.jar:3.2.17.RELEASE]
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:724) ~[spring-web-3.2.17.RELEASE.jar:3.2.17.RELEASE]
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:709) ~[spring-web-3.2.17.RELEASE.jar:3.2.17.RELEASE]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:527) ~[spring-web-3.2.17.RELEASE.jar:3.2.17.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:482) ~[spring-web-3.2.17.RELEASE.jar:3.2.17.RELEASE]
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:455) ~[spring-web-3.2.17.RELEASE.jar:3.2.17.RELEASE]
at com.ams.service.OrderSearcherImpl.searchForName(OrderSearcherImpl.java:49) ~[classes/:na]
at com.ams.order.actions.ViewOrderSearchAction.viewOrderSearch(ViewOrderSearchAction.java:61) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_101]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_101]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_101]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_101]
at ognl.OgnlRuntime.invokeMethod(OgnlRuntime.java:891) [ognl-3.0.6.jar:na]
at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1293) [ognl-3.0.6.jar:na]
at ognl.ObjectMethodAccessor.callMethod(ObjectMethodAccessor.java:68) ~[ognl-3.0.6.jar:na]
at com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor.callMethodWithDebugInfo(XWorkMethodAccessor.java:117) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor.callMethod(XWorkMethodAccessor.java:108) [xwork-core-2.3.20.jar:2.3.20]
at ognl.OgnlRuntime.callMethod(OgnlRuntime.java:1369) [ognl-3.0.6.jar:na]
at ognl.ASTMethod.getValueBody(ASTMethod.java:90) [ognl-3.0.6.jar:na]
at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212) [ognl-3.0.6.jar:na]
at ognl.SimpleNode.getValue(SimpleNode.java:258) [ognl-3.0.6.jar:na]
at ognl.Ognl.getValue(Ognl.java:494) [ognl-3.0.6.jar:na]
at ognl.Ognl.getValue(Ognl.java:458) [ognl-3.0.6.jar:na]
at com.opensymphony.xwork2.ognl.OgnlUtil$2.execute(OgnlUtil.java:309) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.ognl.OgnlUtil.compileAndExecute(OgnlUtil.java:340) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.ognl.OgnlUtil.getValue(OgnlUtil.java:307) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:423) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:287) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:250) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:191) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:167) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:265) [xwork-core-2.3.20.jar:2.3.20]
at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:76) [struts2-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:138) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:191) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:253) [struts2-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:100) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:145) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164) [struts2-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:73) [struts2-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at org.apache.struts2.interceptor.DateTextFieldInterceptor.intercept(DateTextFieldInterceptor.java:125) [struts2-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:91) [struts2-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:139) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:193) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:189) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.20.jar:2.3.20]
at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54) [struts2-core-2.3.20.jar:2.3.20]
at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:564) [struts2-core-2.3.20.jar:2.3.20]
at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81) [struts2-core-2.3.20.jar:2.3.20]
at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99) [struts2-core-2.3.20.jar:2.3.20]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) [catalina.jar:7.0.67]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) [catalina.jar:7.0.67]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:186) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343) [spring-web-3.2.17.RELEASE.jar:3.2.17.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260) [spring-web-3.2.17.RELEASE.jar:3.2.17.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) [catalina.jar:7.0.67]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) [catalina.jar:7.0.67]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) [catalina.jar:7.0.67]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) [catalina.jar:7.0.67]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) [catalina.jar:7.0.67]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) [catalina.jar:7.0.67]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505) [catalina.jar:7.0.67]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169) [catalina.jar:7.0.67]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) [catalina.jar:7.0.67]
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956) [catalina.jar:7.0.67]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) [catalina.jar:7.0.67]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423) [catalina.jar:7.0.67]
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079) [tomcat-coyote.jar:7.0.67]
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625) [tomcat-coyote.jar:7.0.67]
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318) [tomcat-coyote.jar:7.0.67]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_101]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_101]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-coyote.jar:7.0.67]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_101]
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "_embedded" (class org.springframework.hateoas.Resource), not marked as ignorable (2 known properties: , "links", "content"])
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@7583f177; line: 2, column: 18] (through reference chain: org.springframework.hateoas.Resource["_embedded"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:79) ~[jackson-databind-2.2.2.jar:2.2.2]
at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:555) ~[jackson-databind-2.2.2.jar:2.2.2]
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:708) ~[jackson-databind-2.2.2.jar:2.2.2]
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1160) ~[jackson-databind-2.2.2.jar:2.2.2]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:315) ~[jackson-databind-2.2.2.jar:2.2.2]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121) ~[jackson-databind-2.2.2.jar:2.2.2]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2888) ~[jackson-databind-2.2.2.jar:2.2.2]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2094) ~[jackson-databind-2.2.2.jar:2.2.2]
at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readJavaType(MappingJackson2HttpMessageConverter.java:178) ~[spring-web-3.2.17.RELEASE.jar:3.2.17.RELEASE]
... 105 common frames omitted
Problem is your Order class is not having all the fields mapped according to the response, so if you don't want all the fields, ignore them by using Jackson's annotation in your Order class -
@JsonIgnoreProperties(ignoreUnknown = true)
public class Order {
...
}
EDIT: Create your response class like below (you can edit it according yo your json response)-
class Response{
@JsonProperty("_embedded")
private Embedded embedded;
public Embedded getEmbedded() {
return embedded;
}
public void setEmbedded(Embedded embedded) {
this.embedded = embedded;
}
}
class Embedded{
@JsonProperty("orders")
List<Order> orders = new ArrayList<Order>();
public Embedded(){
}
public List<Order> getOrders() {
return orders;
}
public void setOrders(List<Order> orders) {
this.orders = orders;
}
}
class Order {
private int id;
private String name;
private String keyword;
private Agent agent;
@Override
public String toString() {
return "Order [id=" + id + ", name=" + name + ", keyword=" + keyword + "]";
}
public Order(){
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public Agent getAgent() {
return agent;
}
public void setAgent(Agent agent) {
this.agent = agent;
}
}
Since the response is enclosed in the root note _embedded
, have to consider that and annotate with @JsonProperty
, so that it can be recognizable
Get the reponse object like below -
Response reeponse = restOperations
.exchange(new URI(url), HttpMethod.GET, null, new ParameterizedTypeReference<Response>() {
}).getBody().getContent();
You should be able to make exactly the same restTemplate.exchange()
call but using the org.springframework.hateoas.Resources
wrapper class for a Collection of entities rather than the org.springframework.hateoas.Resource
wrapper class for a single entity. I.e. new ParameterizedTypeReference<Resources<Order>>() {}
as the 4th parameter.
Note that if you get an wrapped empty collection returned by this call but with no errors logged, then make sure you have the org.springframework.data:spring-data-rest-core dependency - myself and another developer have just spent much of today debugging this!
(This helped: http://www.java-allandsundry.com/2014/01/consuming-spring-hateoas-rest-service.html)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With