0

What if a XML webservice can respond with different xml structures? Eg an <OkResponse> and an <ErrorResponse>, having completely different fields?

ResponseEntity<Response> rsp = restTemplate
        .postForEntity(url, new HttpEntity<>(xml, HEADERS), OkResponse.class);

Before sending the request, I don't know which type of response will come back. If I'm using OkResponse.class, I will get a ClassCastException if an ErrorResponse is returned.

How could I handle this?

The autogenerated beans are as follows:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso({
    OkResponse.class,
    ErrorResponse.class
})
public class AbstractResponse {

}
membersound
  • 81,582
  • 193
  • 585
  • 1,120
  • I assume that an `ErrorResponse` will only be returned in case of an error. That should mean that the server returns a 4xx or 5xx status code and you get an exception. Which in turn means that the question is moot. In any other case something's wrong and the question is moot as well. – a better oliver Jan 10 '17 at 11:00
  • Unfortunately the xml webservice I'm connecting to serves a http 200 ok even for error responses. And I have not control of the server. This means the question is totally valid. – membersound Jan 10 '17 at 12:12
  • I see. Which library do you use for deserializing? When you use the rest template with `AbstractResponse.class` instead of `OkResponse.class` the deserializer should be able to construct an instance with the correct type and you can use `instanceof` to handle both cases. – a better oliver Jan 10 '17 at 12:25
  • Deserialization is done automatically by the `spring` framework I'm using. I might post the entity as `ResponseEntity`, but then I cannot cast the response to `ResponseEntity` as java forbids this kind of casting. Also I cannot call `(OkResponse) rsp.getBody()`, as reading the body will force the return type to be `AbstractResponse` always, and thus response will never be `instanceof OkResponse` here. – membersound Jan 10 '17 at 12:30

2 Answers2

3

Use String.class

    ResponseEntity<String> rsp = restTemplate
            .postForEntity(url, new HttpEntity<>(xml, HEADERS), String.class);

String responseBody = (String)rsp.getBody();

 Object response=mapper.readValue(responseBody, Class.forName(responseClass))   

Once response body is obtained. make use of service class that you want to map and convert it using jackson mapper .Made use of reflection since the entity passed can be different/dynamic

Barath
  • 5,093
  • 1
  • 17
  • 42
  • But then I'd have to manually convert the xml string. I'd like to keep the `RestTemplate` handling it. – membersound Jan 10 '17 at 10:17
  • please go through this http://stackoverflow.com/questions/9381665/how-can-we-configure-the-internal-jackson-mapper-when-using-resttemplate. – Barath Jan 10 '17 at 10:24
  • 1
    could you try to use Object to capture the response and then check if instance of OkResponse or ErrorResponse and do the proper cast ? – dskfdskjgds Jan 10 '17 at 10:37
  • yes correct, above solution will work if you are dealing only with OkResponse and Error Response .Sample code int httpStatus = rsp.getStatusCode().value(); if ((httpStatus != 0) && ((httpStatus == 200) || (httpStatus == 201))) { OkResponse ok= (OkResponse )rsp.getBody() or mapper.readValue }else{ ErrorResponse err=(ErrorResponse )rsp.getBody() or mapper.readValue } – Barath Jan 10 '17 at 10:42
  • @Barath the code `if... (OkResponse) rsp.getBody(); ... else (ErrorResponse) rsp.getBody()` is absolutely invalid! You can neither cast the `String.class` from the body, nor a common interface of both response classes. Further: where could I get the `mapper` from the restTemplate? I absolutely do not want to configure the jackson mapper myself, but rely on the default spring implementation. – membersound Jan 10 '17 at 10:49
  • @membersound oh my bad sorry for that. yes you have to make use of the mapper to convert it to a response class – Barath Jan 10 '17 at 10:56
  • mapper is an instance of ObjectMapper jackson API. please go through http://stackoverflow.com/questions/25556624/xml-deserialization-to-pojo-using-jackson-xmlmapper(for XML). – Barath Jan 10 '17 at 11:42
0

RestTemplate uses Jackson for JSON serialization, and it supports inherited types though the @JsonTypeInfo annotation. But it requires that all responses have a common 'type' property. If there is no common property that all responses share, then I think you need to use the String approach, and use String.contains() to find a unique property to determine which response type it is.

Klaus Groenbaek
  • 4,820
  • 2
  • 15
  • 30
  • Please see my update above. The beans inherit from a superclass `AbstractResponse`, and have autogenerated xml mappings. As written, they don't share any common fields. – membersound Jan 10 '17 at 12:15
  • You need a common field for it to work out-of-the-box with Jackson. If you have access to the sourcecode, you should just add the extra field. If you think about it, the problem is that although you have a Java inheritance hierarchy, the name is lost as JSON objects are nameless, which is why you need a type attribute. – Klaus Groenbaek Jan 10 '17 at 12:34
  • I have control of the auto generated beans. But not of the xml webservice. Thus even if I introduce a common field, the xml webservice will not fill it. So probably there's no chance for em. – membersound Jan 10 '17 at 12:36