提问人:Nate Riche 提问时间:11/1/2023 最后编辑:Nate Riche 更新时间:11/2/2023 访问量:41
为什么在反序列化 SOAP 响应时总是得到一个不完整的对象?
Why do I consistently get an incomplete object when deserializing a SOAP response?
问:
我有一个 Java 8 Spring Boot,以前称为 WebLogic,它使用 Apache CXF 和 Apache Tomcat。它有几个 SOAP 端点,我想编写 Cucumber 场景来执行集成测试。这些场景使用 Spring 的 RestTemplate 对特定端点进行 HTTP 调用,以验证标头以及响应的部分。为此,我想将服务器的 XML 响应反序列化为 POJO,但我无法让对象正确反序列化。
到目前为止,我能得到的最好的是一个带有正文(反序列化的服务器响应对象)的 ResponseEntity,它只包含我期望的响应的一小部分。例如,我有 POJO(与端点返回的类相同),如下所示:
public class MyServerReply implements Serializable {
private static final long serialVersionUID = 1L;
private ReplyInfo replyInfo;
private String transactionId;
private String version;
private String environment;
private ErrorStatus errorStatus;
public MyServerReply() {
// constructor stuff
}
// getters and setters go here
}
始终如一地,我将按预期填充一个字段,而其他每个字段要么为空,要么为 0。使用具有相同请求正文的 Insomnia 或 Postman 等 HTTP 客户端,我可以看出服务器没有返回这个不完整的响应。同样,考虑我用于进行 HTTP 调用的代码:
public ResponseEntity<MyServerReply> call_getReplyInfo(String arg1, String arg2) {
String soapRequest = String.format(SampleRequestBodies.getReplyInfo, arg1, arg2);
HttpEntity<MyServerReply> requestEntity = new HttpEntity<>(soapRequest, headers);
return restTemplate.exchange(myServerUrl, HttpMethod.POST, requestEntity, MyServerReply.class);
}
如果我修改此方法以返回 ResponseEntity,我可以获得预期的响应,但显然,这给了我一个 String 正文而不是我需要的 POJO。
此特定示例的终结点如下所示:
@WebMethod(operationName = "getReplyInfo")
@WebResult(name = "getReplyInfoMessage", targetNamespace = "http://myNameSpace.com/myApp")
public MyServerReply getReplyInfo(InfoRequest reqObj) {
try {
return myApi.getReplyInfo(reqObj);
} catch (Exception e) {
// exception handling goes here
}
}
服务器响应(省略字段值)如下:
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:getReplyInfoResponse
xmlns:ns2="http://myNameSpace.com/myApp">
<ns2:getReplyInfoMessage>
<replyInfo>
// bunch of response stuff
</replyInfo>
<transId></transId>
<version>1.0.0</version>
<environment>local</environment>
<errorStatus>
// error status fields go here
<uuid>97983440-6c7b-4ca9-a139-328f2de7da1c</uuid> <--bizarrely, this is consistently the only field that is populated when deserializing
</errorStatus>
</ns2:getReplyInfoMessage>
</ns2:getReplyInfoResponse>
</soap:Body>
</soap:Envelope>
请注意,此应用程序的原始 WSDL 文件已丢失。
我已经尝试了几个修复程序。我尝试添加各种javax Xml注释,包括@XmlRootElement,@XmlType以及两者的组合,如下所示:
import javax.xml.bind.annotation.XmlType;
@XmlType(name = "getReplyInfoMessage")
public class MyServerReply {
我还尝试用 @XmlElement 注释上述 POJO 的所有字段,以及没有成功的 getter。这个过程也对 ErrorStatus 和 ReplyInfo 类进行了,但无济于事。
我尝试将 MessageConverter,特别是 jackson-dataformat-xml 中的 MappingJackson2XmlHttpMessageConverter 添加到 restTemplate 并得到相同的不完整结果。
虽然我更希望 ResponseEntity 与我尝试反序列化的 POJO 类型相同,但一般来说,我也无法成功反序列化响应。使用来自同一 jackson 库的 XMLMapper 进行以下设置,我仍然得到一个不完整的对象:
ResponseEntity<String> response = restTemplate.exchange(myServerUrl, HttpMethod.POST, requestEntity, String.class);
XmlMapper xmlMapper = new XmlMapper();
// This is required, since the response includes the outermost Envelope and Body elements and these elements are a part of the POJO I am deserializing to
xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// reply is not null, but it is mostly full of empty fields, excluding the uuid field for whatever reason
reply = xmlMapper.readValue(response.getBody(), MyServerReply.class);
最后,虽然我不想使用这种方法,但我也尝试过使用 JAXB 进行编组/取消编组。但是,在这种情况下,我的请求对象不被服务器理解。我可以根据要求在这篇文章中包含该尝试的详细信息,但最初会省略它,因为它是一种不同类型的错误。
答:
修复 POJO 结构:
确保您的 ) 结构与您收到的结构准确对应。在您的情况下,它包含在信封和正文中,它包括特定的命名空间。Plain Old Java Object (POJO
XML response
XML response
SOAP
您可能需要创建一个充当容器的类来表示完整的 ,涵盖信封、正文和实际数据有效负载。概念:SOAP response
@XmlRootElement(name = "Envelope", namespace = "http://schemas.xmlsoap.org/soap/envelope/")
public class SOAPEnvelope {
@XmlElement(name = "Body", namespace = "http://schemas.xmlsoap.org/soap/envelope/")
private SOAPBody body;
// Getters and Setters
}
@XmlAccessorType(XmlAccessType.FIELD)
public class SOAPBody {
@XmlElement(name = "getReplyInfoResponse", namespace = "http://myNameSpace.com/myApp")
private GetReplyInfoResponse getReplyInfoResponse;
// Getters and Setters
}
@XmlAccessorType(XmlAccessType.FIELD)
public class GetReplyInfoResponse {
@XmlElement(name = "getReplyInfoMessage", namespace = "http://myNameSpace.com/myApp")
private MyServerReply getReplyInfoMessage;
// Getters and Setters
}
接下来,您应该能够将完整的 SOAP 响应转换为类的实例。之后,您将能够检索从中命名的有效负载。SOAPEnvelope
MyServerReply
使用 JAX-RS 客户机进行 SOAP 调用
由于您正在处理服务,因此使用客户端可能更直接,这些客户端比 Spring 的 .SOAP
JAX-RS
SOAP-friendly
RestTemplate
您可以使用 with 来做出和处理响应。下面是一个示例:JAX-RS client
CXF
SOAP calls
WebClient client = WebClient.create("http://your-soap-service-url");
client.accept("text/xml");
Response response = client.post(yourSOAPRequest);
SOAPEnvelope envelope = response.readEntity(SOAPEnvelope.class);
评论