一起学习
越來越多的應用程式使用XML交換訊息。譬如,以 CLDC 為基礎的應用程式 需要去處理 XML 文件,甚至連 HTML 網頁也將會很有可能轉換成為一個以 XML 為基礎的格式。而這意味著從一個網頁伺服器連結一張網頁而取出資料將會需要分析 XML 的技術。
XML就是「可擴展標記語言」,是一種輕便的、以文字為基礎的方法去呈現結構化的資料。
越來越多的應用程式使用 XML 交換訊息,甚且,在一些情況下,您以 CLDC 為基礎的應用程式 (這些是指在以 CLDC 為基礎的架構上執行的,像是微小的手機裝置之類的) 需要去處理 XML 文件。之後,甚至連 HTML 網頁也將會很有可能轉換成為一個以 XML 為基礎的格式,稱為 XHTML。這意味著從一個網頁伺服器連結一張網頁而取出資料將會需要分析 XML 的技術。
尋找一個以 JAVA 為基礎的 XML 分析器不難,尤其是自從有好幾個以 XML 為基礎的的提倡開始被定義為 Java 處理程序的一部分。然而,大部分的 XML 分析器並不支援在 CLDC 所提供的有限資源環境下運作。這些分析器不是使用了太多的記憶體就是使用在 CLDC 下不能運作的 J2SETM 類別。
然而,在這有兩個能在 CLDC 環境下運作的開放原始碼 XML 分析器 ── kXML 和 NanoXML。他們二者的長處有些不同 ── 本文將會討論他們兩者,並且提供您如何去決定哪一個是最適合您的程式需求。然而,您也應該考慮是否選擇 XML,因為 XML 文件是非常冗長的。
如果您能夠控制伺服器端,去使用您自己的二進位的格式去交換資料才是有意義的。
如果一個以 Java 為基礎的客戶端與一個以 Java 為基礎的伺服器端相互溝通,這是相當容易的。在這個實例裡,使用 DataInputStream 和 DataOutputStream 類別,以輕便的方法讀寫取資料。
基本的 XML 分析器型態
基本的 XML 分析器型態有兩種:確認性的和非確認性的。
一個確認性的分析器透過一個文件型別定義或概要 (schema) 確認一份 XML 文件,以確保這份文件的內容是程式所預期的。這個要求將會減低處理的速度。一個非確認性的分析器跳過這個步驟,只保證這個 XML 文件是合乎基本格式的 (well-formed),換而言之,它遵守一般 XML 文件必須遵守的規則。kXML 和 NanoXML 二者都是非確認性的分析器。
非確認性的分析器
XML 分析器也能以他們如何處理和呈現 XML 文件來區分。
NanoXML 是一個單一步驟 (single-step) 分析器。給定一份文件,NanoXML 以一個單一的運作分析它,並將這份文件以一個樹狀的結構傳回。kXML,相較之下,是一個多步驟的 (multi-step) 的分析器 ── 一次分析文件的一塊區域。
這兩種方法各有利弊,如果您正在處理一份龐大的文件,單一步驟的方法使用較多的記憶體,因為全部的文件都儲存在記憶體中。但是單一步驟的方法將較有效率,如果您需要穿越這份文件很多次。多步驟的方法能夠較輕易的處理龐大的文件,但是您必須做較多的簿記 (bookkeeping) 去追蹤您在文件的哪一個地方。
■ 該如何使用 kXML 呢?
從 下載 kXML 原始碼,並且將 kXML 類別包含進您的應用程式。並不是所有的類別都是需要的,所以只要下載包含最小 kXML 的 ZIP 檔。在安裝完這個檔案後,新增下面的引入陳述到您的程式中。
import org.kxml.*;
import org.kxml.parser.*;
|
當您準備好去分析一份文件時,產生一個 XmlParser 類別實體,傳入一個字元 (character) 流當唯一的參數。
try {
Reader r = .....;
XmlParser parser = new XmlParser( r );
}
catch( java.io.IOException e ){
// handle exception....
}
|
如果您的文件是以字串被儲存,舉例來說,您可以藉著轉換字串成為一個位元組 (byte) 陣列來讀取它,然後,結合 InputStreamReader 和 ByteArrayInputStream:
// 讀取字串 (exception handling omitted)
String xml = "some xml";
ByteArrayInputStream bin =
new ByteArrayInputStream( xml.getBytes() );
XmlParser parser = new XmlParser( new InputStreamReader( bin ) );
|
然而,更可能發生的情節是,從網路接收一份文件,舉例來說,在 CLDC 的通常連結架構 (GCF),您使用 MIDP 內建支援 HTTP 的類別來做這件事,然後您會使用 GCF 回傳的輸入流 (input stream),並且轉換它成為字元流 (character stream):
// Read from web (exception handling omitted)
HttpConnection conn = .....;
InputStreamReader doc =
new InputStreamReader( conn.openInputStream() );
XmlParser parser = new XmlParser( doc );
|
析器被產生後,您呼叫它的 read 方法去讀取文件分開的區塊。read 方法對文件的每一個元件回傳一個 ParseEvent 物件:
try {
boolean keepParsing = true;
while( keepParsing ){
ParseEvent event = parser.read();
switch( event.getType() ){
case Xml.START_TAG:
..... // handle start of an XML tag
break;
case Xml.END_TAG:
..... // handle end of an XML tag
break;
case Xml.TEXT:
..... // handle text within a tag
break;
case Xml.WHITESPACE:
..... // handle whitespace
break;
case Xml.COMMENT:
..... // handle comment
break;
case Xml.PROCESSING_INSTRUCTION:
..... // handle XML PI
break;
case Xml.DOCTYPE:
..... // handle XML doctype
break;
case Xml.END_DOCUMENT:
..... // end of document;
keepParsing = false;
break;
}
}
}
catch( java.io.IOException e ){
}
|
ParseEvent 類別為回傳的元件資訊定義了許多方法。getType 方法,舉例來說,回傳元件的型別,像是它是否是一個標籤 (tag) 的開頭、一個註解等。其他的方法提供了額外的訊息,像是一個標籤的內文或一個標籤的屬性。當 END_DOCUMENT 事件被觸發時,分析器便停止運作。
kXML 使得對一個文件遞迴下降的分析方式變的相當容易,分析器的狀態是被呼叫方法維持,遞迴的反映到一個新的分析事件。在簡單的情況下,您可以利用這個方法來保持追蹤您最後一個看過的標籤。
使用 NanoXML,必須下載一些原始碼。官方的 NanoXML 網站是 。
一個修改過的 NanoXML 版本,能夠與 CLDC 並存 (原本的 NanoXML 只適合以 J2SE 為基礎的系統) 可以在 找到。就像 kXML,必須將 NanoXML 包含到您的程式中,然後新增下面的引入陳序到您的程式:
import nanoxml.*;
去分析一份文件,產生一個 kXMLElement 類別的實體,並且選擇使用 parseFromReader、parseString 或 parseCharArray 其中之一:
由於 NanoXML 是一個單一步驟的分析器,它分析全部的文件,並且轉換它成為一個樹狀的XML元件。樹狀圖的根部是您所產生的 kXMLElement 實體,且每一個樹狀圖的節點是另一個 kXMLElement 實體。您可以使用像是 getChildren、getTagName 和 getContents 的方法穿越這棵樹。
去示範如何分析,這裡提供一個 MIDP 程式。填滿一個字串,包含了許多 XML 的元件,這個程式使用 kXML。以 NanoXML 分析的話,將呼叫 parseUsingNanoXML 這行的註解符號 (//) 去掉,並將呼叫 parseUsingkXML 這行標記為註解。這個程式的一個完整的專案也是可以利用的 ,你可以使用 J2ME Wireless Toolkit 來執行這個專案。
package com.ericgiguere.techtips;
import java.io.*;
import java.util.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import nanoxml.*;
import org.kxml.*;
import org.kxml.parser.*;
/**
* Simple MIDlet that demonstrates how an XML document can be
* parsed using kXML or NanoXML.
*/
public class XMLTest extends MIDlet {
// Our XML document -- normally this would be something you
// download.
private static String xmlDocument =
"- apple
"
"- orange
"
"- pear
";
private Display display;
private Command exitCommand = new Command( "Exit",
Command.EXIT, 1 );
public XMLTest(){
}
protected void destroyApp( boolean unconditional )
throws MIDletStateChangeException {
exitMIDlet();
}
protected void pauseApp(){
}
protected void startApp() throws MIDletStateChangeException {
if( display == null ){ // first time called...
initMIDlet();
}
}
private void initMIDlet(){
display = Display.getDisplay( this );
String [] items;
//items = parseUsingNanoXML( xmlDocument );
items = parseUsingkXML( xmlDocument );
display.setCurrent( new ItemList( items ) );
}
public void exitMIDlet(){
notifyDestroyed();
}
// Parses a document using NanoXML, looking for
// "item" nodes and returning their content as an
// array of strings.
private String[] parseUsingNanoXML( String xml ){
kXMLElement root = new kXMLElement();
try {
root.parseString( xml );
Vector list = root.getChildren();
Vector items = new Vector();
for( int i = 0; i < list.size(); i ){
kXMLElement node =
(kXMLElement) list.elementAt( i );
String tag = node.getTagName();
if( tag == null ) continue;
if( !tag.equals( "item" ) ) continue;
items.addElement( node.getContents() );
}
String[] tmp = new String[ items.size() ];
items.copyInto( tmp );
return tmp;
}
catch( kXMLParseException ke ){
return new String[]{ ke.toString() };
}
}
// Parses a document using kXML, looking for "item"
// nodes and returning their content as an
// array of strings.
private String[] parseUsingkXML( String xml ){
try {
ByteArrayInputStream bin =
new ByteArrayInputStream(
xml.getBytes() );
InputStreamReader in = new InputStreamReader( bin );
XmlParser parser = new XmlParser( in );
Vector items = new Vector();
parsekXMLItems( parser, items );
String[] tmp = new String[ items.size() ];
items.copyInto( tmp );
return tmp;
}
catch( IOException e ){
return new String[]{ e.toString() };
}
}
private void parsekXMLItems( XmlParser parser, Vector items )
throws IOException {
boolean inItem = false;
while( true ){
ParseEvent event = parser.read();
switch( event.getType() ){
case Xml.START_TAG:
if( event.getName().equals( "item" ) ){
inItem = true;
}
break;
case Xml.END_TAG:
if( event.getName().equals( "item" ) ){
inItem = false;
}
break;
case Xml.TEXT:
if( inItem ){
items.addElement( event.getText() );
}
break;
case Xml.END_DOCUMENT:
return;
}
}
}
// Simple List UI component for displaying the list of
// items parsed from the XML document.
class ItemList extends List implements CommandListener {
ItemList( String[] list ){
super( "Items", IMPLICIT, list, null );
addCommand( exitCommand );
setCommandListener( this );
}
public void commandAction( Command c, Displayable d ){
if( c == exitCommand ){
exitMIDlet();
}
}
}
}
|
下载本文示例代码
在以CLDC為基礎的架構上分析XML在以CLDC為基礎的架構上分析XML在以CLDC為基礎的架構上分析XML在以CLDC為基礎的架構上分析XML在以CLDC為基礎的架構上分析XML在以CLDC為基礎的架構上分析XML在以CLDC為基礎的架構上分析XML在以CLDC為基礎的架構上分析XML在以CLDC為基礎的架構上分析XML在以CLDC為基礎的架構上分析XML在以CLDC為基礎的架構上分析XML在以CLDC為基礎的架構上分析XML