Java有多種方法可以分析XML文檔,你可以選擇現在已經成熟的標準技術,比如DOM和SAX,或者你可以選擇專用于處理XML的Java API (Java API for XML Processing,JAXP)。
JAXP是一種專門提供XML文檔解析的Java接口,下面我們就來研究一下如何使用Apache Xerces-2 解析器來實現JAXP。
JAXP提供了一種DOM及SAX方式的解析器來處理XML文檔,也就是工廠模式。你選擇不同的工廠類就會有不同的處理方法。工廠類實際上是一個標準設計模式,你可以根據需要自行修改。
利用JAXP,你可以使用DocumentBuilderFactory來建立自己的DocumentBuilder類,或者使用SAXParserFactory來建立自己的SAXParser類。不同之處就在于DOM解析器是將整個文檔讀入內存并允許你以隨機方式讀取文檔,而SAX解析器是通過呼叫句柄來解釋XML文檔數據的。下面我們仔細研究一下DocumentBuilder類。
通過在DocumentBuilderFactory類中呼叫newDocumentBuilder方法,我們可以建立一個DocumentBuilder類。你可以通過呼叫newInstance方法來建立多個DocumentBuilderFactory類。
例如,你可以這樣建立一個新的DocumentBuilderFactory類:
DocumentBuilderFactorydbfactory = DocumentBuilderFactory.newInstance();
一旦有了工廠類的句柄,你就可以馬上建立一個DOM解析器的實例了。下面是建立代碼:
DocumentBuilder builder = dbfactory. newDocumentBuilder();
這樣我們就建立了一個真正的DocumentBuilder類的實例。為了解析文檔,你必須調用DocumentBuilder類的解析方法。解析方法會返回一個Document對象,就是你要操作的XML文檔。
Listing A實現了一個簡單的利用DocumentBuilderFactory和DocumentBuilder類的方法:
Listing A:
JAXPSample.java
import javax.xml.parsers.*;
import org.w3c.dom.*;
public class JAXPSample {
public static void main(String[] args) {
String filename = "sample.xml";
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder parser = factory.newDocumentBuilder();
Document d = parser.parse(filename);
}
catch (Exception e) {
System.err.println("Exception: " + e.getMessage());
}
}
}
DocumentBuilder類其實就是一個DOM解析器。利用JAXP的DocumentBuilder類的優勢就在于它比其他XML解析器更輕便。
當通過DocumentBuilder接口使用DOM時,解析器會返回一個Document類。這個Document類很重要,因為它是完全符合W3C標準的,這意味這你可以將這個Document類和其他DOM解析器良好的結合起來。
例如你可以通過以下代碼找回元素值:
String getXMLValue(Document doc, String name) {
NodeListnlist=doc.getElementsByTagName(name);
String value = nlist.item(0).getFirstChild().getNodeValue();
return value;
}
這個方法用來尋找文檔內與字符串一致的子節點。
以Sun公司的JAXP為例來看看其中和DOM相關的包:
DOM包結構
org.w3c.com:定義了DOM的接口。這是w3c所指制定的DOM規范,因為DOM規范是與語言無關的,因而其中只是定義了接口,而沒有實現任何地類。任何具體的DOM實現需要有其它的類庫給出。
javax.xml.parser:定義了DocumentBuilderFactory類DocumentBuilder類。編程時用DocumentBuilderFactory來生成一個具體的和具體的類庫相聯系的DocumentBuilder類的實例,然后再由這個DocumentBuilder對象來生成一個Document實例。Document對象中就包含了DOM的樹模型結構,是DOM模型的根。同SAXParserFactory一樣,具體的DocumentBuilder對象的建立,取決于一個環境變量javax.xml.parsers.DocumentBuilderFactory的值,同樣的,也可以直接把類名傳遞給DocumentBuilderFactory來建立一個DocumentBuilder。
com.sun.xml.tree:Sun的Java XML的DOM實現,他對標準的DOM提供了一些擴展,其中大部分的東西并不是DOM標準。包括了XMLDocument,ElementNode, XMLDocumentBuilder和TreeWalker等類。XMLDocument類實現了DOM的Document界面,它同樣也提供了靜態的工廠方法來建立一個Document對象。ElementNode類代表了在一個DOM樹中的每一個節點元素,并且它經常被繼承,來實現一些自定的功能。 而XMLDocumentBuilder實際上是一個DocumentHandler類型的類,也就是說,它接受一個SAX解析器傳遞過來的事件,然后根據這些事件來在內存中建立一個DOM模型。
DOM處理過程
DOM的處理過程相對SAX而言要簡單些,先用DocumentBuilderFactory的靜態方法newInstance()建立一個DocumentBuilderFactory的實例,然后用它的newDocumentBuilder()方法建立一個DocumentBuilder。然后可以用DocumentBuilder的parser()方法來解析一個XML文檔并建立DOM模型。在JAXP中還提供了更為方便的功能擴展,就是使用XMLDocument。你可以為一個SAX解析器注冊一個事件處理器XMLDocumentBuilder,在解析過后,可以調用XMLDocumentBuilder的getDocument()方法就可以把一個外在地XML文檔轉換成一個內存中的DOM樹了,并生成一個Document(XMLDocument)對象,后面的工作,就是調用XMLDocument對象和ElementNode對象的各個方法來對DOM樹進行操作了。最后,還可以調用XMLDocument的wirte()方法來把DOM樹輸出為XML文件。因為在標準的DOM模型中并沒有提供與write()相類似的方法,所以要處理輸出的話,用XMLDocument會更方便些。
實例DOM
下面我們可以來看看例子了。
首先當然是import語句:
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.DocumentBuilder;
為了能給你更多的印象,我詳細內出了所有的類,實際上你只需要一句就可以了:
import javax.xml.parsers.*;
你還需要的是W3C對DOM和DOM例外的定義:
import org.w3c.dom.Document; import org.w3c.dom.DOMException;
還有一些其他的用來進行例外和I/O處理的類:
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import java.io.File;
import java.io.IOException;
因為要輸出XML文檔,所以還要引入XMLDocument:
import com.sun.xml.tree.XmlDocument;
public class DomEcho{
在程序邏輯中,因為要處理DOM模型,所以當然首先應該申明一個Document對象:
static Document document;
public static void main (String argv [])
{
if (argv.length != 1) {
System.err.println ("Usage: java DomEcho filename");
System.exit (1);
}
用DocumentBuilderFactory類的靜態方法newInstance()來創建一個工廠實例,之所以稱為工廠,是由于再這兒應用到了設計模式(Design Pattern)中的工廠模式,實際上在Java類庫中設計模式的范例隨處可見,如果能夠對設計模式有一些了解,就會很容易為Java龐雜的類庫理出一條條脈絡分明的經線。
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.parse( new File(argv[0]) );
前面說過,DOM標準模型中并沒有定義輸出的接口,因而如果需要進行文檔輸出的話,就需要用到JAXP的擴展,使用到XmlDocument。這兒有一個較為簡便的方法,就是用強制類型轉換,把一個Document類轉換成XmlDocument,然后,就可以使用XmlDocument的write()方法了:
XmlDocument xdoc = (XmlDocument) document;
xdoc.write (System.out);
在后面的都是例外的處理了,在DOM中雖然有定義了DOMException,但是DOMException只是用在遍歷和操作DOM樹時引發的例外。在解析文檔和初始化解析器時所引發的例外,還是借助于SAX中的例外處理方法,以使程序上具有一致性:
} catch (SAXParseException spe) {
// 處理解析過程中生成的例外
System.out.println ("\n** Parsing error"
+ ", line " + spe.getLineNumber ()
+ ", uri " + spe.getSystemId ());
System.out.println(" " + spe.getMessage() );
Exception x = spe;
if (spe.getException() != null)
x = spe.getException();
x.printStackTrace();
} catch (SAXException sxe) {
// 處理一般的程序例外或者解析器初始化時引發的例外
Exception x = sxe;
if (sxe.getException() != null)
x = sxe.getException();
x.printStackTrace();
} catch (IOException ioe) {
// IO例外
ioe.printStackTrace();
}
}
這而對DOM講的比較簡略,但是基本的程序結構我們已經可以從中看出來了。
ILayerDescriptions layerdec = mapdescription.LayerDescriptions;
for(int i=0;i < mapdescription.LayerDescriptions.Count; i++)
{
ILayerDescription onelayerdesc = layerdec.get_Element(i);
onelayerdesc.Visible = true;
}
[C#寫法]
private void LabelField(IFeatureLayer pFeatureLayer,IServerContext pServerContext)
{
IGeoFeatureLayer pGeoFeatureLayer = pFeatureLayer as IGeoFeatureLayer;
pGeoFeatureLayer.AnnotationProperties.Clear();
IAnnotateLayerPropertiesCollection pAnnoLayerPropsColl = pGeoFeatureLayer.AnnotationProperties;
ILabelEngineLayerProperties pLabelEngine;
pLabelEngine = pServerContext.CreateObject("esriCarto.LabelEngineLayerProperties") as ILabelEngineLayerProperties;
pLabelEngine.Expression = "[Field]";
IAnnotateLayerProperties pAnnoLayerProps = pLabelEngine as IAnnotateLayerProperties;
pAnnoLayerPropsColl.Add(pAnnoLayerProps);
pGeoFeatureLayer.DisplayAnnotation = true;
ArcGIS Server Java講座---使用圖片和TrueType字體進行標注 |
直奔主題吧,接上個主題的講座的內容,講講如何用圖片進行標注。在前面一個講座我們已經說明,如何對選中的物體進行高亮顯示。而且這一部分工作是在ADF這一端完成的。那么有的時候,我們進行高亮顯示的時候,不僅僅希望只是設置顏色,我們希望能夠用圖片或者truetype字體進行標注。比如在犯罪地點放一個壞人之類的功能。
我們來看看實現原理,關鍵是兩個類,WebTrueTypeMarkerSymbol和WebPictureMarkerSymbol,沒啥好說的,直接用代碼來說明吧: 先來看看普通的點標注: WebPointpt=(WebPoint)arg0.getWebGeometry().toMapGeometry(arg0.getWebContext().getWebMap());
WebSimpleMarkerSymbol markers =null; markers = new WebSimpleMarkerSymbol(); markers.setAntialiasing(true); markers.setColor("255,0,0"); markers.setWidth(8); markers.setOutlineColor("255,0,0"); markers.setMarkerType(WebSimpleMarkerSymbol.CIRCLE); markers.setPicture(bytInput); GraphicElement ge=new GraphicElement(); ge.setGeometry(pt); ge.setSymbol(markers); WebGraphicsgraphics=arg0.getWebContext().getWebGraphics(); graphics.addGraphics(ge); arg0.getWebContext().refresh(); 注意webgraphicsymbol的setPicture方法的參數不是圖片目錄,而是圖片的二進制數組,所以需要用文件IO把圖片讀取進來。當然,如果用戶訪問量很大,線程就不安全了,大家可以在application啟動時進行讀取,放在context的某個attribute里面。我原來認為是通過設置路徑方式實現,這樣又可能可以搞定gif圖形的閃爍,但是現在實驗結果是不行。設置圖片標注的代碼如下: WebPointpt=(WebPoint)arg0.getWebGeometry().toMapGeometry(arg0.getWebContext().getWebMap());
//圖片在servelet容器里面目錄 String picPath=FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath()+"\\images\\angle.gif"; File myFile = new File(picPath); FileInputStream myStream= newFileInputStream(myFile); BufferedInputStream buf = newBufferedInputStream(myStream); byte[] bytInput = newbyte[(int)myFile.length()]; buf.read(bytInput, 0, (int) myFile.length()); buf.close(); myStream.close(); WebPictureMarkerSymbol markers=newWebPictureMarkerSymbol(); markers.setPicture(bytInput); GraphicElement ge=new GraphicElement(); ge.setGeometry(pt); ge.setSymbol(markers); WebGraphics graphics=arg0.getWebContext().getWebGraphics(); graphics.addGraphics(ge); arg0.getWebContext().refresh(); 在jsf文件里面添加如下代碼,調用圖片標注工具,進行測試: <a:tool id="pointTest"defaultImage="images/point.gif"hoverImage="images/pointU.gif"
selectedImage="images/pointD.gif"clientAction="EsriMapPoint" serverAction="com.cj.ucdemo.GifTestTool"clientPostBack="true"/> 圖片標注的結果如下: ![]() 用圖片做markSymbol 我們也可以使用TrueType字體里面的矢量字體對圖形進行符號化顯示,這也是我們經常使用的方法,具體代碼如下:
把上面的代碼放在一個點擊工具里面,在jsf文件中用如下代碼進行調試:
TrueType字體標注的結果如下圖所示: |