开发某运营商的WebService订购关系通知接口,原先由VAC直接推送,现在中间加了一个网关,VAC推送网关,网关推送给我们,结果解析网关给过来的XML是出现了问题,一抓包发现是请求里头多了一些MultiRef的标签,不符合原先的WSDL定义。比如说userIdType,按正常来说应该是:
<userIdType type="soapenc:int">1</userIdType>
但是实际拿到的却是:
<userIdType href="#id1"/>
指向的是一个id1的标签,需要从下文里头去找出具体的值:
<multiRef id="id1" soapenc:root="0" soapenv:encodingStyle=" "http://schemas.xmlsoap.org/soap/encoding/">http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="soapenc:int">1</multiRef>
大概搜了一下,说其实是AXIS的一个特有的东西,甚至可以说是一个bug,AXIS2里头都已经取消了multiRef,要在AXIS1里头去掉这个也容易,只要改一下配置就可以了,于是联系网关的人,但是人家说这种int型就是用multiRef来传的,至于让他们改配置,自然不现实,人家说你用AXIS来解析好了。。。没办法,只能自己来弄了。
AXIS肯定不想用,这样的话就得大改。找了一下,网上有方法可以直接把格式转换一下,但是似乎略显复杂,而且接口相对于原来的程序而言也不太合适,于是还是决定自己解析。原先只是根据WSDL生成了请求对象,内容是使用SAX来解析的,大概的代码就是这么几行:
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
VACReqHander handler = new VACReqHander();
parser.parse(in, handler);
主要就是这个VACReqHander,它继承了org.xml.sax.helpers.DefaultHandle,并且重载了其中的一下方法,这些方法会在适当的时候被parser调用。主要的方法有:
public void startDocument() //开始时被调用
public void startElement(String uri, String localName, String qName, Attributes attributes) //元素开始时被调用
public void characters(char[] ch, int start, int length) //取到值的时候被调用
public void endElement(String uri, String localName, String qName) //元素结束时被调用
public void endDocument() //结束时被调用
原先的实现比较简单,在startElement()里头把key放到一个全局的变量里头,即:key = qName,然后再characters()里头根据key来判断具体是哪个元素并且赋值,比如:
if ("userIdType".equals(key))
req.setUserIdType(Integer.valueOf(data));
但是这种只能处理标准格式的数据,如<key>value</key>
,但是类似<userIdType href="#id1"/>
这种数据的话characters()
根本就没有被调用。大概debug了一下,发现这些都被放在startElement()
的attributes
对象里头。这样就好办了,首先定义几个变量来保存数据:
String userIDtypeHref = null; //userIdType元素里的href对应的值,去掉#号,这里即id1
String multiRefID = null; //multiRef元素里的id对应的值,这里即id1
Hashtable<String,int> multiRefs = new Hashtable<String,Internet>(); //存储multiRef里头id和值的对应关系,这里即id1=1
然后在startElement()
里头设置变量:
if(qName.equals("userIdType")){
userIDtypeHref = attributes.getValue("href").substring(1);
}
if(qName.equals("multiRef")){
multiRefID = attributes.getValue("id");
}
然后在 characters()
里取得multiRef里的value,并存储到Hashtable:
if ("multiRef".equals(nodeName)) {
if (refid!=null && isDigit(data))
{
multiRefs.put(multiRefID, Integer.valueOf(data));
}
}
最后在endDocument()
设置一下userIdType的值:
if (req.getUserIdType()==0 && userIDtypeHref!=null)
req.setUserIdType(multiRefs.get(userIDtypeHref));
其他int类型的值,比如updateType可以同样处理