(一)解析第三方接口封装为对象

Yujee 2016-09-20 02:38:27
最近做了一个还算可以上手的外包项目,就是解析第三方接口的数据然后展示在页面中,整个项目做下来还是有很多收获的,学到的有很多,包括xml的解析,echarts曲线的应用,video标签的应用及添加定时任务器定时刷新数据库等,耗时将近两个月,在这里先做一个简单的整理,后续还会继续完善。
1、得到第三方给的开放接口说明文档。
1)、接口地址:
http://对方ip:8080/my/service/TestService?wsdl
2)、接口安全:
出于安全的考虑:接收到的接口消息都会检验用户名和密码,故接口都需要带Head。
3)、接口方法:
接口方法中都会给出参数和返回值。如:
sendMsg:
参数:account String 用户登录名
返回值:unitId 单元UUID
unitName 单元名称

WSDL是Web Services Description Language的缩写,是一个用来描述Web服务和说明如何与Web服务通信的XML 语言。它的工具是wsdl2java,这时就需要用wsdl2java生成客户端代码。
生成客户端常用的大概就是三种方式:CXF、axis2、axis,Axis2 是Axis的后续版本,是新一代的SOAP引擎。使用axis提供的工具类org.apache.axis.wsdl.WSDL2Java先生成客户端stub,然后像使用本地方法一样调用远程接口。这里我用到的是CXF,生成一大堆java文件,具体我也没怎么看明白都是什么 ,后来领导让我用axis2来生成,再后来也还是没有用到,直接问第三方人员要了两个文件进行wsdl接口实现,拉出了第三方的xml数据。

CXF生成客户端的过程:
引用
1、下载apache-cxf-3.1.7工具
2、环境配置path及CXF-HOME路径。
3、cmd中输入wsdl2java验证是否配置成功。
4、输入命令wsdl -d +(生成代码所在目录) -client +(路径)

附录:
-d:指定生成的客户端生成 目录
-p:指定生成的客户端包名
-client:指定生成客户端调用类, 即包含main方法调用客户端方法的类
-t:为代码生成测试用例
-g:生成服务端和客户端代码
-ssi:为服务端实现代码生成接口类

2、wsdl生成xml文档
1>将URL及账户信息封装到properties文件。利用ResourceBundle调用。
config.properties:

uri=http://对方ip:8080/my/service/TestService?wsdl
otherAccount=me
otherPassword=123456
2>
ClientAuthInterceptor:

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.namespace.QName;
import java.util.List;

public class ClientAuthInterceptor extends AbstractSoapInterceptor
{
private String userName;
private String password;

public ClientAuthInterceptor()
{
super(Phase.WRITE);
}

public ClientAuthInterceptor(String userName, String password)
{
super(Phase.PREPARE_SEND);
this.userName = userName;
this.password = password;
}

public String getUserName()
{
return userName;
}

public void setUserName(String userName)
{
this.userName = userName;
}

public String getPassword()
{
return password;
}

public void setPassword(String password)
{
this.password = password;
}

public void handleMessage(SoapMessage msg) throws Fault
{
List<Header> headers = msg.getHeaders();
// 创建Document对象
Document doc = DOMUtils.createDocument();
Element ele = doc.createElement("authHeader");
// 配置服务器端Head信息的用户密码
Element eleId = doc.createElement("username");
eleId.setTextContent(userName);
Element elePass = doc.createElement("password");
elePass.setTextContent(password);
ele.appendChild(eleId);
ele.appendChild(elePass);
// 生成的XML文档
/**
* <authHeader>
* <userName>用户名</userName>
* <password>密码</password>
* </authHeader>
*/
headers.add(new Header(new QName(""), ele));
}
}

3>
OpenInterface:

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
public class OpenInterface {
public static final String CONFIG_FILE_NAME = "config";
//自动扫描properties文件名为“config”的文件
ResourceBundle bundle = PropertyResourceBundle.getBundle(CONFIG_FILE_NAME);
//将“config.properties”文件封装到bundle中。
public void getMsg() {
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
Client client = dcf.createClient(bundle.getString("uri"));
//引用bundle获取config中的uri即wsdl的地址。
client.getOutInterceptors().add(new ClientAuthInterceptor(bundle.getString("otherAccount"),
bundle.getString("otherCmpLoginPassword")));
//调用ClientAuthInterceptor,获取到设定的用户名和密码
try {
Object array = client.invoke("sendMsg", "xxx");
//sendMsg为第三方的接口方法,“xxx”为此方法所传入的参数。
int length = java.lang.reflect.Array.getLength(array);
List<Object> list = new ArrayList<Object>();
for (int i = 0; i < length; i++) {
list.add(java.lang.reflect.Array.get(array, i));
}
String str = (length == 0 ? "" : list.get(0).toString());
System.out.print{str};
}
}

此时,就会在控制台输出一个xml文件,接口测试也就成功,接下来就该解析xml了。

3、解析xml
解析xml有很多种方式,有DOM,DOM4J,SAX,jaxb等,我用到的是DOM4J和jaxb,jaxb可以快速解析xml,DOM4J可以从遍历节点来一个一个解析着手。如:
引用
<?xml version="1.0" encoding="UTF-8"?>
<DeviceType>
<combos>
<combo>
<attr>--</attr>
<id>01</id>
<text>温度器</text>
</combo>
<combo></combo>
</combos>
<DeviceType>
(1)每个数据都有所对应的节点名称故用jaxb。
引用
<?xml version="1.0" encoding="UTF-8"?>
<entry>
<string>11223243342345</string>
<map>
<entry>
<string>time</string>
<list>
<string>2016-08-31 00:00:47</string>
<string>2016-08-31 08:28:07</string>
</list>
</entry>
<entry>
<string>温度器2</string>
</entry>
<entry>
<string>value</string>
<list>
<float>33.0</float>
<float>33.0</float>
</list>
</entry>
</map>
</entry>
</deviceMap>
</ChartDataResponse>(2)
这时节点名称与关键字重合,无法建立对应的对象,故使用DOM4J解析。
这里,我先讲jaxb的解析:
1>
JaxbUtil:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
/**
* Jaxb2工具类
*/
public class JaxbUtil {
/**
* JavaBean转换成xml
* 默认编码UTF-8
* @param obj
* @param writer
* @return
*/
public static String convertToXml(Object obj) {
return convertToXml(obj, "UTF-8");
}
/**
* JavaBean转换成xml
* @param obj
* @param encoding
* @return
*/
public static String convertToXml(Object obj, String encoding) {
String result = null;
try {
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);

StringWriter writer = new StringWriter();
marshaller.marshal(obj, writer);
result = writer.toString();
} catch (Exception e) {
e.printStackTrace();
}

return result;
}

/**
* xml转换成JavaBean
* @param xml
* @param c
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T converyToJavaBean(String xml, Class<T> c) {
T t = null;
try {
JAXBContext context = JAXBContext.newInstance(c);
Unmarshaller unmarshaller = context.createUnmarshaller();
t = (T) unmarshaller.unmarshal(new StringReader(xml));
} catch (Exception e) {
e.printStackTrace();
}
return t;
}
}

2>创建xml所对应的对象类。
EnvionmentType:

import javax.xml.bind.annotation.*;
import java.util.List;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name ="DeviceType") //xml的根目录
@XmlType(propOrder ={"combo"}) // 解析xml所有的对象属性
public class EnvionmentType {
@XmlElementWrapper(name = "combos") //此对象属性包含于combos中
@XmlElement(name = "Combo") // 此对象属性在xml中的映射
private List<Combo> combo; //属性combo
public List<Combo> getCombo() {
return combo;
}
public void setCombo(List<Combo> combo) {
this.combo = combo;
}
@Override
public String toString() {
return "DeviceTypeResponse"+combo;
}
}

Combo:

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder = { "id","text","attr" })
public class Combo {
@XmlElement(name = "id")
private String id; //编号
@XmlElement(name = "text")
private String text; //名称
@XmlElement(name = "attr")
private String attr; //单位
public String getAttr() {
return attr;
}
public void setAttr(String attr) {
this.attr = attr;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
@Override
public String toString() {
return "Combo [id=" + id + ",text=" + text + ",attr=" + attr+"]";
}
}

注:对象属性一定要与xml的节点名所对应,否则就会报错,若想新建一个属性不存在xml中,则在属性上加 @XmlTransient或者@XmlAttribute。
3>解析
OpenInterface:

EnvionmentType envionmentType = JaxbUtil.converyToJavaBean(str, EnvionmentType.class);
System.out.println(envionmentType);

此时数据就已解析出来,封装为envionmentType对象。
4>将对象存入数据库中
OpenInterface:
@Autowired
private EnvionmenttypeService envionmenttypeService;
for (Combo m : envionmentType.getCombo()) {
Combo combo = new Combo();
combo.setId(m.getId());
combo.setText(m.getText());
combo.setAttr(m.getAttr());
envionmenttypeService.save(combo);
}
...全文
429 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

67,512

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧