Digester解析XML

1.背景

很久就想写这样的一篇博客了,记得以前的一个项目里面有个接口是安排我做的,当时就是利用http请求,封装的sSOAP信息,然后返回的也是SOAP信息,最终的达到的目的是解析这个请求返回的SOAP信息,然后返回他们想要的数据格式,那时候是我第一次遇到解析SOAP格式的信息,一点没头绪,在网上查了很多,也很混乱,不过已经有了一个大致的方向,这样自己就开始着手慢慢的试着去解析,经过几天的斗争,终于搞定(时间有点长哈~),这次项目又遇到了,我倒是不怕了,但是一看返回的SOAP的格式和以前的略有不同,但是大体思路还是有的,现在我写这个博客的目的就是想分享一自己遇到的几种soap格式信息的解析方式,但是答题的思路还是一致的。

2.准备工作

Digester来解析,因此我们需要引入包:

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-digester3</artifactId>
<version>3.2</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>

3.第一种

返回的SOPA信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<work:WorkContext xmlns:work="http://oracle.com/weblogic/soap/workarea/">rO0ABXduADR3ZWJsb2dpYy5hcHAuU0hDVF9lQ2hhbm5lbF9BY2NvdW50X1NlcnZpY2VfTmV3V2ViU3ZjAAAA1gAAACN3ZWJsb2dpYy53b3JrYXJlYS5TdHJpbmdXb3JrQ29udGV4dAAJMS4wLjAuMC4wAAA=</work:WorkContext>
</S:Header>
<S:Body>
<ns2:NewCreateAccountAddress_eChannelInputResponse xmlns:ns2="http://siebel.com/">
<NewCreateAccountAddress_eChannelOutput>
<Address_Desc/>
<Address_Id/>
<Error_Code>849133</Error_Code>
<Error_Message>客户未找到</Error_Message>
</NewCreateAccountAddress_eChannelOutput>
</ns2:NewCreateAccountAddress_eChannelInputResponse>
</S:Body>
</S:Envelope>

分析返回的SOAP信息:这里可以看到,这个里面就包含了四个元素,这是最简单的soap信息了,包含的信息就一层,因此我们新建一个QueryAccountAddressInfoInput类包含着四个元素,作为类的属性。

NewCreateAccountAddres

1
2
3
4
5
6
7
8
public class NewCreateAccountAddress {
private String addressDesc;
private String addressId;
private String errorCode;
private String errorMessage;
//...此处省略getter和setter方法
}

新建一个中间类,用于保存解析之后返回的对象

NewCreateAccountAddressCache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class NewCreateAccountAddressCache {
private NewCreateAccountAddress newCreateAccountAddress;
public void addNewCreateAccountAddress(NewCreateAccountAddress newCreateAccountAddress) {
this.setNewCreateAccountAddress(newCreateAccountAddress);
}
public NewCreateAccountAddress getNewCreateAccountAddress() {
return newCreateAccountAddress;
}
public void setNewCreateAccountAddress(NewCreateAccountAddress newCreateAccountAddress) {
this.newCreateAccountAddress = newCreateAccountAddress;
}
}

这里的意思就是将返回的信息在保存到我们的实体类里面

下面建一个解析类:

DigesterUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class DigesterUtils {
public static NewCreateAccountAddressCache addressCachedigester( String str) throws Exception {
//新建一个Digester对象
Digester digester = new Digester();
//不进行XML与相应的DTD的合法性验证
digester.setValidating(false);
// digester.addObjectCreate(url,class) URL是一个规则,当遇到这个规则制定的标签的时候,会创建一个类NewCreateAccountAddressCache,并将其压如栈顶。
digester.addObjectCreate("S:Envelope/S:Body/ns2:NewCreateAccountAddress_eChannelInputResponse", NewCreateAccountAddressCache.class);
//根据URL规则,创建一个NewCreateAccountAddress类,并将压如栈中
digester.addObjectCreate("S:Envelope/S:Body/ns2:NewCreateAccountAddress_eChannelInputResponse/NewCreateAccountAddress_eChannelOutput", NewCreateAccountAddress.class);
//下面的这几句就是给NewCreateAccountAddress的属性复制,如果属性的名字和标签的名字不一样,那么后面第二个参数要跟上属性名字,如果一样,就不用写第二个参数,直接写第一个URL规则就行了
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:NewCreateAccountAddress_eChannelInputResponse/NewCreateAccountAddress_eChannelOutput/Address_Desc", "addressDesc");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:NewCreateAccountAddress_eChannelInputResponse/NewCreateAccountAddress_eChannelOutput/Address_Id", "addressId");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:NewCreateAccountAddress_eChannelInputResponse/NewCreateAccountAddress_eChannelOutput/Error_Code", "errorCode");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:NewCreateAccountAddress_eChannelInputResponse/NewCreateAccountAddress_eChannelOutput/Error_Message", "errorMessage");
//addsetNext()方法,就是根绝uRL规则中 最后一个标签和前一个标签之间的动作,当每次遇到<NewCreateAccountAddress_eChannelOutput>这个标签的时候,都要执行setNewCreateAccountAddress()方法 ,这个方法定义在NewCreateAccountAddressCache类里面。其实这个方法就是每次遇到符合这个url规则的标签都会去执行父节点的一个方法。
digester.addSetNext("S:Envelope/S:Body/ns2:NewCreateAccountAddress_eChannelInputResponse/NewCreateAccountAddress_eChannelOutput", "addNewCreateAccountAddress");
NewCreateAccountAddressCache vc = null;
try {
InputStream codeForXml = new ByteArrayInputStream(str.getBytes());
//xml实体的单一输入源
InputSource input = new InputSource(codeForXml);
//将解析之后的对象赋予给NewCreateAccountAddressCache
vc = (NewCreateAccountAddressCache) digester.parse(input);
} catch (IOException e) {
throw new Exception(e);
} catch (SAXException e) {
throw new Exception(e);
}
return vc;
}

最后的使用:

1
2
3
4
5
6
7
8
9
NewCreateAccountAddress newCreateAccountAddress = new NewCreateAccountAddress();
try {
NewCreateAccountAddressCache nc = DigesterUtils.addressCachedigester(soapResponseContent);
newCreateAccountAddress = nc.getNewCreateAccountAddress();
} catch (Exception e) {
e.printStackTrace();
}
//测试是否将标签的值付给了类的属性
LOG.debug("newCreateAccountAddress====="+newCreateAccountAddress.getErrorMessage());

2.第二种

soap信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<work:WorkContext xmlns:work="http://oracle.com/weblogic/soap/workarea/">rO0ABXdnAC13ZWJsb2dpYy5hcHAuU0hDVF9Bc3NldF9BZGRyZXNzX1NlcnZpY2VXZWJTdmMAAADWAAAAI3dlYmxvZ2ljLndvcmthcmVhLlN0cmluZ1dvcmtDb250ZXh0AAkxLjAuMC4wLjAAAA==</work:WorkContext>
</S:Header>
<S:Body>
<ns2:QueryAccountAddressInfoInputResponse xmlns:ns2="http://siebel.com/">
<QueryAccountAddressInfoOutput>
<AccountAddressInfo>
<Account>
<CutAddress>
<AddressName>南汇区康桥镇沪南公路2688弄250号401室</AddressName>
<Descriptor/>
<Id>1-1J97-568</Id>
<SHCTIBPAddressId/>
<ServiceBureauName>南汇</ServiceBureauName>
<SubBureauName>康桥</SubBureauName>
</CutAddress>
<CutAddress>
<AddressName>港沿镇富裕路43号</AddressName>
<Descriptor/>
<Id>1-Q8I-2372</Id>
<SHCTIBPAddressId/>
<ServiceBureauName>崇明</ServiceBureauName>
<SubBureauName>港沿</SubBureauName>
</CutAddress>
</Account>
</AccountAddressInfo>
</QueryAccountAddressInfoOutput>
</ns2:QueryAccountAddressInfoInputResponse>
</S:Body>
</S:Envelope>

分析soap信息:在标签里面有两个一样的,因此我们将标签对应的所有子元素作为实体的属性

新建类:CutAddress.java

1
2
3
4
5
6
7
8
9
10
public class CutAddress {
private String addressName;
private String descriptor;
private String id;
private String sHCTIBPAddressId;
private String serviceBureauName;
private String subBureauName;
//....此处省略getter,setter方法
}

新建一个中间类用于保存soap解析之后赋予的对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class QueryAccountAddressInfoCache {
private List areaList = new ArrayList();
public List getAreaList() {
return areaList;
}
public void setAreaList(List areaList) {
this.areaList = areaList;
}
// 供Digester调用的方法
public void addCutAddress(CutAddress cutAddress) {
this.areaList.add(cutAddress);
}
}

新建解析类:

DigesterUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class DigesterUtils {
public static QueryAccountAddressInfoCache digester( String str) throws Exception {
Digester digester = new Digester();
digester.setValidating(false);
digester.addObjectCreate("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account", QueryAccountAddressInfoCache.class);
// 指明匹配模式和要创建的类
digester.addObjectCreate("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress", CutAddress.class);
// 设置对象属性,与xml文件对应,不设置则是默认
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress/AddressName", "addressName");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress/Descriptor", "descriptor");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress/ID", "id");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress/SHCTIBPAddressId", "sHCTIBPAddressId");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress/ServiceBureauName", "serviceBureauName");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress/SubBureauName", "subBureauName");
// 当移动到下一个标签中时的动作 ,每次遇到CutAddress标签都会执行这个动作
digester.addSetNext("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress", "addCutAddress");
QueryAccountAddressInfoCache vc = null;
try {
InputStream codeForXml = new ByteArrayInputStream(str.getBytes());
//xml实体的单一输入源
InputSource input = new InputSource(codeForXml);
vc = (QueryAccountAddressInfoCache) digester.parse(input);
} catch (IOException e) {
throw new Exception(e);
} catch (SAXException e) {
throw new Exception(e);
}
return vc;
}
}

使用:

1
2
3
QueryAccountAddressInfoCache vc = DigesterUtils.digester(str);
//得到就是那几个相同的CutAddress标签
list<CutAddress>list = vc.getAreaList();

3.第三种

soap信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<work:WorkContext xmlns:work="http://oracle.com/weblogic/soap/workarea/">rO0ABXdnAC13ZWJsb2dpYy5hcHAuU0hDVF9Bc3NldF9BZGRyZXNzX1NlcnZpY2VXZWJTdmMAAADWAAAAI3dlYmxvZ2ljLndvcmthcmVhLlN0cmluZ1dvcmtDb250ZXh0AAkxLjAuMC4wLjAAAA==</work:WorkContext>
</S:Header>
<S:Body>
<ns2:QueryAccountAddressInfoInputResponse xmlns:ns2="http://siebel.com/">
<QueryAccountAddressInfoOutput>
<AccountAddressInfo>
<Account>
<CutAddress>
<AddressName>南汇区康桥镇沪南公路2688弄250号401室</AddressName>
<Descriptor/>
<Id>1-1J97-568</Id>
<SHCTIBPAddressId/>
<ServiceBureauName>南汇</ServiceBureauName>
<SubBureauName>康桥</SubBureauName>
</CutAddress>
<CutAddress>
<AddressName>港沿镇富裕路43号</AddressName>
<Descriptor/>
<Id>1-Q8I-2372</Id>
<SHCTIBPAddressId/>
<ServiceBureauName>崇明</ServiceBureauName>
<SubBureauName>港沿</SubBureauName>
</CutAddress>
<CSN>202120024066</CSN>
<RowId>1-1PP-4135</RowId>
<SerialNum>189JZB01</SerialNum>
</Account>
</AccountAddressInfo>
</QueryAccountAddressInfoOutput>
</ns2:QueryAccountAddressInfoInputResponse>
</S:Body>
</S:Envelope>

分析soap信息:
这个Account 标签下面有几个单独的元素CSN,RowId,SerialNum 还有2个一样的CutAddress,一次我们新建类:

QueryAccountAddressInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class QueryAccountAddressInfo {
private String CSN;
private String rowId;
private String serialNum;
private List<CutAddress>cutAddressList = new ArrayList<CutAddress>();
public String getCSN() {
return CSN;
}
public void setCSN(String cSN) {
CSN = cSN;
}
public String getRowId() {
return rowId;
}
public void setRowId(String rowId) {
this.rowId = rowId;
}
public String getSerialNum() {
return serialNum;
}
public void setSerialNum(String serialNum) {
this.serialNum = serialNum;
}
public List<CutAddress> getcutAddressList() {
return cutAddressList;
}
//这个方法用于添加CutAddress
public void addCutAddress(CutAddress cutAddress) {
cutAddressList.add(cutAddress);
}

CutAddress

1
2
3
4
5
6
7
8
9
public class CutAddress {
private String addressName;
private String descriptor;
private String id;
private String sHCTIBPAddressId;
private String serviceBureauName;
private String subBureauName;
//...这里省略getter和setter方法
}

新建解析类:

DigesterUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class DigesterUtils {
public static QueryAccountAddressInfo digester( String str) throws Exception {
Digester digester = new Digester();
digester.setValidating(false);
//如果根据url规则创建的对象里面还有子元素,那么要先赋值,不能直接创建下一个对象,例如下面的样子,创建对象之后,如果有要复制的属性,必须要现在赋值好
digester.addObjectCreate("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account", QueryAccountAddressInfo.class);
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CSN");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/RowId","rowId");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/SerialNum","serialNum");
digester.addObjectCreate("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress",CutAddress.class);
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress/AddressName","addressName");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress/Descriptor","descriptor");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress/Id","id");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress/SHCTIBPAddressId","sHCTIBPAddressId");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress/ServiceBureauName","serviceBureauName");
digester.addBeanPropertySetter("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress/SubBureauName","subBureauName");
digester.addSetNext("S:Envelope/S:Body/ns2:QueryAccountAddressInfoInputResponse/QueryAccountAddressInfoOutput/AccountAddressInfo/Account/CutAddress","addCutAddress");
QueryAccountAddressInfo vc = null;
try {
InputStream codeForXml = new ByteArrayInputStream(str.getBytes());
//xml实体的单一输入源
InputSource input = new InputSource(codeForXml);
vc = (QueryAccountAddressInfo) digester.parse(input);
} catch (IOException e) {
throw new Exception(e);
} catch (SAXException e) {
throw new Exception(e);
}
return vc;
} }

使用:

QueryAccountAddressInfo 这个就是最后的我们想要的实体类了。

4.HTTP请求SOAP

请求的信息是SOAP,返回的也是SOAP

soapResponseContent = HttpClientUtil.postHttpRequest(queryAccountAddressInfoPath, soapRequestContent, “application/soap+xml”, “utf-8”, null);

这里的方法有的参数没有,就设置为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
*@common 采用apache commons httpclient实现 http发送post请求调用
*@param url : 请求路径
*@param content : 要传输的数据
*@param contentType : 传送数据的格式 (
* text/html;
* application/soap+xml;
* application/xml;
* application/json;
* application/javascript)
*@param charset : 字符集编码
*@return
*@throws Exception
*/
public static String postHttpRequest(String url, String content, String contentType, String charset, Map<String,String> header) throws Exception {
LOG.info("【解密接口路径】---->" + url);
LOG.info("【解密接口参数】---->" + content);
PostMethod postMethod = new PostMethod(url);
RequestEntity requestEntity = new StringRequestEntity(content, contentType, charset);
postMethod.setRequestEntity(requestEntity);
HttpMethodParams params = postMethod.getParams();
//使用系统提供的默认的恢复策略
//params.setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
//设置超时时间30秒
params.setParameter(HttpMethodParams.SO_TIMEOUT, 30000);
//设置HTTP请求头
if (header != null) {
Set<Entry<String, String>> entrySet = header.entrySet();
for (Entry<String, String> next : entrySet) {
postMethod.setRequestHeader(next.getKey(), next.getValue());
}
}
HttpClient httpClient = new HttpClient();
int executeMethod = httpClient.executeMethod(postMethod);
LOG.debug("postHttpRequest HttpStatus.SC_OK="+executeMethod);
// 获取response内容
if (HttpStatus.SC_OK == executeMethod) {
String responseBody = postMethod.getResponseBodyAsString();
return responseBody;
}
return null;
}
坚持原创技术分享,您的支持将鼓励我继续创作!

分享
Fork me on GitHub