Как использовать RestTemplate с разными ResponseEntities?

Что, если веб-сервис XML может отвечать разными XML-структурами? Например, <OkResponse> и <ErrorResponse> с совершенно разными полями?

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

Перед отправкой запроса я не знаю, какой тип ответа вернется. Если я использую OkResponse.class, я получу ClassCastException, если будет возвращено ErrorResponse.

Как я мог справиться с этим?

Автогенерируемые bean-компоненты выглядят следующим образом:

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

}

person membersound    schedule 10.01.2017    source источник
comment
Я предполагаю, что ErrorResponse будет возвращено только в случае ошибки. Это должно означать, что сервер возвращает код состояния 4xx или 5xx, и вы получаете исключение. Что в свою очередь означает, что вопрос спорный. В любом другом случае что-то не так и вопрос тоже спорный.   -  person a better oliver    schedule 10.01.2017
comment
К сожалению, веб-служба xml, к которой я подключаюсь, обслуживает http 200 даже для ответов об ошибках. И у меня нет контроля над сервером. Это означает, что вопрос полностью актуален.   -  person membersound    schedule 10.01.2017
comment
Я понимаю. Какую библиотеку вы используете для десериализации? Когда вы используете оставшийся шаблон с AbstractResponse.class вместо OkResponse.class, десериализатор должен иметь возможность создавать экземпляр с правильным типом, и вы можете использовать instanceof для обработки обоих случаев.   -  person a better oliver    schedule 10.01.2017
comment
Десериализация выполняется автоматически фреймворком spring, который я использую. Я мог бы опубликовать объект как ResponseEntity<AbstractResponse>, но тогда я не могу привести ответ к ResponseEntity<OkResponse>, поскольку java запрещает такой тип приведения. Также я не могу вызвать (OkResponse) rsp.getBody(), так как чтение тела заставит возвращаемый тип всегда быть AbstractResponse, и, таким образом, ответ здесь никогда не будет instanceof OkResponse.   -  person membersound    schedule 10.01.2017


Ответы (2)


Используйте 1_

    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))   

После получения тела ответа. используйте класс обслуживания, который вы хотите сопоставить, и преобразуйте его с помощью сопоставителя Джексона. Используется отражение, поскольку переданный объект может быть другим/динамическим

person Barath    schedule 10.01.2017
comment
Но тогда мне пришлось бы вручную преобразовать строку xml. Я бы хотел, чтобы этим занимался RestTemplate. - person membersound; 10.01.2017
comment
пожалуйста, просмотрите этот stackoverflow.com/questions/9381665/. - person Barath; 10.01.2017
comment
не могли бы вы попытаться использовать Object для захвата ответа, а затем проверить, есть ли экземпляр OkResponse или ErrorResponse, и выполнить правильное приведение? - person dskfdskjgds; 10.01.2017
comment
да, верно, приведенное выше решение будет работать, если вы имеете дело только с OkResponse и Error Response. Образец кода int httpStatus = rsp.getStatusCode().value(); if ((httpStatus != 0) && ((httpStatus == 200) || (httpStatus == 201))) { OkResponse ok= (OkResponse )rsp.getBody() или mapper.readValue }else{ ErrorResponse err=(ErrorResponse )rsp.getBody() или mapper.readValue } - person Barath; 10.01.2017
comment
@Barath код if... (OkResponse) rsp.getBody(); ... else (ErrorResponse) rsp.getBody() абсолютно недействителен! Вы не можете ни привести String.class из тела, ни общий интерфейс обоих классов ответа. Далее: где взять mapper из restTemplate? Я совершенно не хочу настраивать jackson mapper самостоятельно, а полагаюсь на реализацию Spring по умолчанию. - person membersound; 10.01.2017
comment
@membersound о, прости меня за это. да, вы должны использовать картограф, чтобы преобразовать его в класс ответа - person Barath; 10.01.2017
comment
mapper — это экземпляр ObjectMapper jackson API. пожалуйста, пройдите через stackoverflow.com/questions/25556624/ XML). - person Barath; 10.01.2017

RestTemplate использует Jackson для сериализации JSON и поддерживает унаследованные типы через аннотацию @JsonTypeInfo. Но для этого требуется, чтобы все ответы имели общее свойство «тип». Если нет общего свойства, которое разделяют все ответы, то я думаю, вам нужно использовать подход String и использовать String.contains(), чтобы найти уникальное свойство, чтобы определить, какой это тип ответа.

person Klaus Groenbaek    schedule 10.01.2017
comment
Пожалуйста, смотрите мое обновление выше. Компоненты наследуются от суперкласса AbstractResponse и имеют автоматически сгенерированные отображения XML. Как написано, они не имеют общих полей. - person membersound; 10.01.2017
comment
Вам нужно общее поле, чтобы оно работало «из коробки» с Джексоном. Если у вас есть доступ к исходному коду, вы должны просто добавить дополнительное поле. Если подумать, проблема в том, что хотя у вас есть иерархия наследования Java, имя теряется, поскольку объекты JSON безымянны, поэтому вам нужен атрибут типа. - person Klaus Groenbaek; 10.01.2017
comment
У меня есть контроль над автоматически сгенерированными бобами. Но не веб-сервиса xml. Таким образом, даже если я введу общее поле, веб-сервис xml его не заполнит. Так что, вероятно, у них нет шансов. - person membersound; 10.01.2017