我需要在我的 apache cxf 端点中返回未转义的 xml 字符串。下面我粘贴了我的代码部分、当前返回的 xml 字符串和所需的 xml 字符串:
@WebService(endpointInterface = "net.system.soapservice.result.ResultMgrPortType", serviceName = "ResultMgrPortType")
public class ResultServiceImpl implements ResultMgrPortType {
@Override
public String genericAPIResult(String resultMsg) {
Map<String, String> processedResultMsg = DataFormatUtil.parseResultMsg(resultMsg);
String response = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">"
+ "<soapenv:Body>"
+ "<req:ResponseMsg"
+ " xmlns:req=\"http://api-v1.gen.mm.vodafone.com/mminterface/request\"><![CDATA[<?xml"
+ " version=\"1.0\""
+ "encoding=\"UTF-8\"?><response"
+ " xmlns=\"http://api-"
+ "v1.gen.mm.vodafone.com/mminterface/response\">"
+ "<ResponseCode>"+processedResultMsg.get("ResultCode")+"</ResponseCode>"
+ "<ConversationID>"+processedResultMsg.get("ConversationID")+"</ConversationID>"
+ "<ResponseDesc>Service result processed successfully.</ResponseDesc>"
+ "<OriginatorConversationID>"+processedResultMsg.get("OriginatorConversationID")+"</OriginatorConversationID>"
+ "<ServiceStatus>0</ServiceStatus>"
+ "</response>]]></req:ResponseMsg>"
+ "</soapenv:Body>"
+ "</soapenv:Envelope>";
return response;
}
}
在我的日志中可以看到来自上述端点的 xml 响应:
ID: 4
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml
Headers: {}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ResponseMsg xmlns="http://api-v1.gen.mm.vodafone.com/mminterface/result"><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<req:ResponseMsg
xmlns:req="http://api-v1.gen.mm.vodafone.com/mminterface/request"><![CDATA[<?xml
version="1.0"
encoding="UTF-8"?><response
xmlns="http://api-
v1.gen.mm.vodafone.com/mminterface/response"><ResponseCode>0</ResponseCode><ConversationID>AG_20170411_000059047aba809d6631</ConversationID><ResponseDesc>Service result processed successfully.</ResponseDesc><OriginatorConversationID>902004_fhltd_60575.0</OriginatorConversationID><ServiceStatus>0</ServiceStatus></response>]]></req:ResponseMsg>
</soapenv:Body>
</soapenv:Envelope></ResponseMsg></soap:Body></soap:Envelope>
--------------------------------------
客户端期望的 xml:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ResponseMsg xmlns="http://api-v1.gen.mm.vodafone.com/mminterface/result">
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<req:ResponseMsg xmlns:req="http://api-v1.gen.mm.vodafone.com/mminterface/request"><![CDATA[<?xml version="1.0"encoding="UTF-8"?><response xmlns="http://api-v1.gen.mm.vodafone.com/mminterface/response"><ResponseCode>0</ResponseCode><ConversationID>AG_20170406_00004ddc3d70f6ba93f0</ConversationID><ResponseDesc>Service result processed successfully.</ResponseDesc><OriginatorConversationID>902004_fhltd_94802.0</OriginatorConversationID><ServiceStatus>0</ServiceStatus></response>]]></req:ResponseMsg>
</soapenv:Body>
</soapenv:Envelope>
</ResponseMsg>
</soap:Body>
</soap:Envelope>
注意:响应中不应包含
<
和 >
。也许我应该在 apache-cxf-services.xml
文件中添加一些配置或创建一个拦截器来更改此行为。预先感谢您的时间和精力。
我在这里回答我自己的问题。解决方案是创建一个拦截器来强制cxf中的jaxb对CDATA中的响应字符串进行编码。因此,我从立即响应中删除了 CDATA(现在应该由拦截器处理),结果响应应该类似于:
String response = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">"
+ "<soapenv:Body>"
+ "<req:ResponseMsg"
+ " xmlns:req=\"http://api-v1.gen.mm.vodafone.com/mminterface/request\"><?xml"
+ " version=\"1.0\""
+ "encoding=\"UTF-8\"?><response"
+ " xmlns=\"http://api-"
+ "v1.gen.mm.vodafone.com/mminterface/response\">"
+ "<ResponseCode>"+processedResultMsg.get("ResultCode")+"</ResponseCode>"
+ "<ConversationID>"+processedResultMsg.get("ConversationID")+"</ConversationID>"
+ "<ResponseDesc>Service result processed successfully.</ResponseDesc>"
+ "<OriginatorConversationID>"+processedResultMsg.get("OriginatorConversationID")+"</OriginatorConversationID>"
+ "<ServiceStatus>0</ServiceStatus>"
+ "</response></req:ResponseMsg>"
+ "</soapenv:Body>"
+ "</soapenv:Envelope>";
然后创建一个拦截器以将 CDATA 添加回 PRE_STREAM 阶段:
import java.io.OutputStream;
import javax.xml.stream.XMLStreamWriter;
import org.apache.cxf.interceptor.AttachmentOutInterceptor;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.staxutils.StaxUtils;
public class CdataWriterInterceptor extends AbstractPhaseInterceptor<Message> {
public CdataWriterInterceptor() {
super(Phase.PRE_STREAM);
addAfter(AttachmentOutInterceptor.class.getName());
}
@Override
public void handleMessage(Message message) {
System.out.println("############# CdataWriterInterceptor Executed #######################");
message.put("disable.outputstream.optimization", Boolean.TRUE);
XMLStreamWriter writer
= StaxUtils.createXMLStreamWriter(message.getContent(OutputStream.class));
message.setContent(XMLStreamWriter.class, new CDataXMLStreamWriter(writer));
}
}
最后在CDataXMLStreamWriter中添加CDATA,如下所示:
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.cxf.staxutils.DelegatingXMLStreamWriter;
public class CDataXMLStreamWriter extends DelegatingXMLStreamWriter {
private String currentElementName;
public CDataXMLStreamWriter(XMLStreamWriter del) {
super(del);
}
@Override
public void writeCharacters(String text) throws XMLStreamException {
System.out.println("WritingCData" + text);
super.writeCData(text);
}
private boolean checkIfCDATAneededForCurrentElement() {
if ("MessageBody".equals(currentElementName)) {
return true;
}
return false;
}
@Override
public void writeStartElement(String prefix, String local, String uri) throws XMLStreamException {
currentElementName = local;
super.writeStartElement(prefix, local, uri);
}
}
这个想法是,如果 xml 字符串在 CDATA 部分内发送,则 cxf 内部 xml 解析器不会解析它,因此特殊字符不会被转义。这对我有用,我希望对其他人也有用。更多详细信息请参见:CXF jaxb send string as CData
7 年后,这实际上帮助我解决了转义所有不需要的字符的问题。
这是我的解决方案,希望它可以帮助有需要的人,同时,如果有人可以改进它,他们将值得我尊重!
package com.rc.rcib.siocs.proship.bridge.interceptor;
import org.apache.commons.io.IOUtils;
import org.apache.cxf.interceptor.AttachmentOutInterceptor;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
public class SpecialCharsInterceptor extends AbstractPhaseInterceptor<Message> {
public SpecialCharsInterceptor() {
super(Phase.PRE_STREAM);
addAfter(AttachmentOutInterceptor.class.getName());
}
@Override
public void handleMessage(Message message) {
InputStream content = message.getContent(InputStream.class);
String contentString = null;
try {
contentString = IOUtils.toString(content, StandardCharsets.UTF_8);
contentString = contentString.replace("&", "&");
} catch (Exception e) {
throw new RuntimeException(e);
}
message.setContent(InputStream.class, IOUtils.toInputStream(contentString, StandardCharsets.UTF_8));
}
}