一、两种访问方法: 目前的kxml支持两种wap格式:WBXML/WML。 而有两种方法将解析WBXML: 1。使用j2me将WBXML转换到XML; 2。使用kxml直接解析WBXML流。下面我在这里讨论一下使用第二种方法实现client代码解析WBXML,当然要使用kxml了。 二、kxml实现方法: 首先需要位于web server的应用程序通过开放WAP网关(关于JWAP:详见http://jwap.sourceforge.net/)发送WML文件给j2me client。在WAP网关将数据发送j2me client之前WAP网关将WML文件转换为了WBXML文件。下面代码的展示了j2me client如何接收WBXML数据,解析数据,并显示有用的数据在手机屏幕上。 需要注意,在本例程中使用的kxml v1.0版本,kxml v2.0版本在使用上可能有所不同,开发者可以参考kxml2的手册。 import java.io.*; import org.kxml.*; import org.kxml.parser.*; import org.kxml.wap.*; import javax.microedition.lcdui.*; import javax.microedition.midlet.*; import javax.microedition.io.*; public class WbxmlTest extends MIDlet implements CommandListener { private Display display = null; private List menu = null; private Form form = null; private String incomingText = ""; static final Command okCommand = new Command("Ok", Command.OK, 1); static final Command exitCommand = new Command("Exit", Command.EXIT, 0); // This is a hard coded WSP message that contains // address of web server where our jsp page is located. byte[] message ={ (byte)'1',(byte)0x40,(byte)0x3D,(byte)'h',(byte)'t', (byte)'t',(byte)'p',(byte)':',(byte)'/',(byte)'/', (byte)'l',(byte)'o',(byte)'c',(byte)'a',(byte)'l', (byte)'h',(byte)'o',(byte)'s',(byte)'t',(byte)':', (byte)'8',(byte)'0',(byte)'8',(byte)'0',(byte)'/', (byte)'e',(byte)'x',(byte)'a',(byte)'m',(byte)'p', (byte)'l',(byte)'e',(byte)'s',(byte)'/',(byte)'j', (byte)'s',(byte)'p',(byte)'/',(byte)'f',(byte)'i', (byte)'n',(byte)'a',(byte)'l',(byte)'f',(byte)'i', (byte)'l',(byte)'e',(byte)'s',(byte)'/',(byte)'D', (byte)'a',(byte)'t',(byte)'.',(byte)'j',(byte)'s', (byte)'p',(byte)0x80,(byte)0x94,(byte)0x88,(byte)0x81, (byte)0x6A,(byte)0x04,(byte)0x83,(byte)0x99 }; // Memory space to receive message. byte[] msg = new byte [256]; public void pauseApp() { /* ----- */ } public void destroyApp(boolean unconditional) { notifyDestroyed(); } public void startApp() { display = Display.getDisplay(this); this.mainMenu(); }//startApp //Displays the menu screen private void mainMenu() { menu = new List(" Send Request", Choice.IMPLICIT); menu.append(" Send Message",null); menu.addCommand(okCommand); menu.setCommandListener(this); display.setCurrent(menu); }//mainMenu //Display the reply from WAPGateway (JWap). private void showReply() { form = new Form( "Incoming Message" ); form.append("The price = " + incomingText); form.addCommand(exitCommand); form.setCommandListener(this); display.setCurrent(form); }//showReply // Makes a WSP Connection with a WAPGateway, // Sends a message and receives the reply. public void getConnect() { Datagram dgram =null; DatagramConnection dc=null; try { dc = (DatagramConnection)Connector.open ("datagram://127.0.0.1:9200"); dgram = dc.newDatagram(message, message.length); try{ dc.send(dgram);} catch (InterruptedIOException e){ e.printStackTrace(); } dgram = dc.newDatagram (msg,msg.length); try{ dc.receive(dgram);} catch (InterruptedIOException e){ e.printStackTrace();} catch( IOException e){ e.printStackTrace();} // This is the most interesting part. incomingText = this.getIncomingTextOfWmlc(dgram.getData()); this.showReply(); dc.close(); }//try catch (IllegalArgumentException ie){ ie.printStackTrace(); } catch (ConnectionNotFoundException cnf){ cnf.printStackTrace(); } catch (IOException e){e.printStackTrace();} }//getConnect() private String getIncomingTextOfWmlc ( byte[] wmlc ) { try { // Remove WSP header. // We know it is 19 bytes for our case. // But for real world applications, // this should be dynamically deteced. for ( int j = 0; j < wmlc.length-19; j++ ) wmlc[j] = wmlc[j+19]; WmlParser parser = new WmlParser(new ByteArrayInputStream(wmlc)); while (true) { try { ParseEvent parseEvent = parser.read(); if ( parseEvent.getType() == Xml.START_TAG ) { Attribute attr = parseEvent.getAttribute("value"); if ( attr != null ) return attr.getValue(); }//if }//try catch ( IOException e) {} }//while }//try catch ( IOException e) { e.printStackTrace(); } return "error"; }//getIncomingTextOfWmlc public void commandAction(Command c, Displayable d) { String commandlabel = c.getLabel(); if (commandlabel.equals("Exit")) destroyApp(false); else if (commandlabel.equals("Ok")) getConnect(); }//commandAction }//class WbxmlTest 为了演示目的,除了建立一个web Server外,还要在本机建立一个JWAP Server。 三、代码说明: 上面的代码将数据连接请求发送到了本机的JWAP Server的URL:“datagram://127.0.0.1:9200”,并发送了一个硬编码的WSP(wireless Session Protocol)请求:http://localhost:8080/examples/jsp/finalfiles/Dat.jsp,然后等待并读取JWAP Server的回应,在接收到回应信息后使用kxml解析提取其中的数据(元素属性名为“value”的属性值)。在解析完成后,将数据显示于手机屏幕上。 代码中的getConnect 方法建立与JWAP Server的连接,并发送请求给JWAP Server,要求访问web Server上的http://localhost:8080/examples/jsp/finalfiles/Dat.jsp,在接收到JWAP Server发回的请求后,getConnect方法调用getIncomingTextOfWmlc方法提取接收到的WBXML数据。由于j2me client与JWAP Server之间的通讯使用了WAP协议堆栈,所以j2me client接收的数据中包含WSP头,在getIncomingTextOfWmlc方法中首先去掉了这个WSP头。 之后,getIncomingTextOfWmlc方法使用KXML的事件解析机制进行了4步操作: 1。传入保存WBXML数据的字节数组构造WmlParser 对象; 2。调用WmlParser的read方法,找到第一个TAG开始的地方; 3。读取“value”属性值; 4。回到第2步进行2、3之间的循环,直到找不到START_TAG。 四、数据流程: 而在JWAP网关接收到j2me client发来的硬编码请求后,将这个请求转发给了web Server,本例程中的web Server为http://localhost:8080。web Server接收到请求后,使用一个硬编码的WML文件作为回应: <?xml version="1.0"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"> <%@ page language="java" contentType= "text/vnd.wap.wml" %> <wml> <card id="c0" newcontext="false" ordered="false"> <input type="Price" value="15224" emptyok="false"/> </card> </wml> 当JWAP网关接收到这个web Server的WML文件后,将其转换为WBXML格式并修改其content-type编码为WBXML,最后将转换后的WBXML格式数据发给了j2me client。 五、总结: 使用kxml方法避免了XML与WBXML之间的相互转换,WBXML文件的格式减少了XML文件的大小,不仅可将WBXML用于WAP设备,也可以用于基于web的程序与无线设备之间的通讯和数据交换。 |