最近项目需要接入动环监控设备,使用WebService走B接口调用。调用格式Soap,参数为XML格式,为此记录一下XML转JavaBean,JavaBean转XML,以及从Soap报文中提取XML,生成Soap的方法等。
1.引入主要依赖
~~
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
~~
2.Soap工具类
具体Soap的参数格式需要看具体参数
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.imr.common.util.XmlUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringEscapeUtils;
import javax.xml.soap.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@Slf4j
public class SoapUtil {
/**
* 构建请求体-前部分
*
* @return
* @throws
*/
public static String generateSoap(String xml) {
try {
// 创建空的SOAP消息
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage soapMessage = messageFactory.createMessage();
// 获取SOAP消息的SOAP部分
SOAPPart soapPart = soapMessage.getSOAPPart();
// 创建SOAP消息的SOAP Envelope
SOAPEnvelope envelope = soapPart.getEnvelope();
//删除默认的前缀
envelope.removeNamespaceDeclaration("SOAP-ENV");
// 设置SOAP Envelope的命名空间
envelope.addNamespaceDeclaration("soap", "http://schemas.xmlsoap.org/soap/envelope/");
envelope.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
envelope.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
envelope.setPrefix("soap");
//去掉头节点
SOAPHeader header = envelope.getHeader();
header.detachNode();
// 创建SOAP消息的SOAP Body
SOAPBody body = envelope.getBody();
body.setPrefix("soap");
SOAPElement invoke = body.addChildElement("invoke", "", "http://SUService.chinatelecom.com");
SOAPElement xmlData = invoke.addChildElement("xmlData", "", "");
xmlData.addTextNode(xml);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
soapMessage.writeTo(outputStream);
return StringEscapeUtils.unescapeXml(outputStream.toString());
} catch (Exception e) {
log.error("构造SOAP参数失败:{}", xml, e);
}
return "";
}
/**
* 生成soap并且发送请求-返回响应的soap报文
*
* @param
*/
public static <T> String sendRequest(T obj, String url) {
String xml = XmlUtil.javaBeanToXml(obj);
String soap = generateSoap(xml);
HttpRequest post = HttpUtil.createPost(url);
post.contentType(ContentType.XML.getValue());
log.info(soap);
return post.body(soap).execute().body();
}
/**
* 从返回的soap中提取(body)-xml
*
* @param
* @return
*/
public static String getResponseXml(String soapXml) {
try {
// 创建SOAP消息工厂
MessageFactory factory = MessageFactory.newInstance();
// 创建SOAP消息
SOAPMessage message;
ByteArrayInputStream inputStream = new ByteArrayInputStream(soapXml.getBytes());
message = factory.createMessage(null, inputStream);
// 提取SOAP消息的Body部分
SOAPBody body = message.getSOAPBody();
return body.getTextContent();
} catch (Exception e) {
log.error("获取SOAP参数失败:{}", soapXml, e);
}
return "";
}
}
3.XML解析工具类
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
@Slf4j
public class XmlUtil {
private XmlUtil() {
}
private static final XmlMapper XM = new XmlMapper();
static {
// 对象的所有字段全部列入,还是其他的选项,可以忽略null等
XM.setSerializationInclusion(JsonInclude.Include.ALWAYS);
// 设置Date类型的序列化及反序列化格式
XM.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// 忽略空Bean转json的错误
XM.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
// 忽略未知属性,防止json字符串中存在,java对象中不存在对应属性的情况出现错误
XM.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 注册一个时间序列化及反序列化的处理模块,用于解决jdk8中localDateTime等的序列化问题
XM.registerModule(new JavaTimeModule());
//设置时区
XM.setTimeZone(TimeZone.getTimeZone("GMT+8"));
}
public static <T> T xmlToJavaBean(String xmlStr, Class<T> clz) {
try {
return XM.readValue(xmlStr, clz);
} catch (Exception e) {
log.error("XML序列化失败", e);
return null;
}
}
public static <T> T xmlToJavaBean(String xmlStr, TypeReference<T> valueTypeRef) {
try {
return XM.readValue(xmlStr, valueTypeRef);
} catch (Exception e) {
log.error("XML序列化失败", e);
return null;
}
}
public static String javaBeanToXml(Object o) {
try {
return XM.writeValueAsString(o);
} catch (Exception e) {
log.error("XML序列化失败", e);
return null;
}
}
}
4. 创建类用来接收XML解析后的参数
XML格式
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JacksonXmlRootElement(localName = "root")
public class RequesttParam {
@JacksonXmlProperty(localName = "EXECUTE_ORDER")
private String excuTeOrder;
@JacksonXmlProperty(localName = "TT_ID")
private String ttId;
@JacksonXmlProperty(localName = "SrcFlag")
private String srcFlag;
@JacksonXmlProperty(localName = "FinishTime")
private String finishTime;
@JacksonXmlProperty(localName = "Longitude")
private String longitude;
@JacksonXmlProperty(localName = "Latitude")
private String latitude;
@JacksonXmlProperty(localName = "OperatorName")
private String operatorName;
}
有的XML格式比较复杂,节点比较多,或者节点上仍然有元素,比如下面这这些
对于节点上有元素点的,比如Device上有元素ID,这种情况只需要在之前注解上加个属性即可
@JacksonXmlProperty(localName = "ID",isAttribute = true)
private String deviceId;
而对于节点下有多个节点的情况,JackSon一样提供了解决办法,如下
@JacksonXmlElementWrapper(useWrapping=false)
@JacksonXmlProperty(localName = "SPID")
private List<String> spid;
如有误,请理解!