轉帖|其它|編輯:郝浩|2010-08-26 15:18:11.000|閱讀 953 次
概述:前兩天有在忙乎著關于解析XML文件的項目,有了不少收獲,特意記錄下來,希望與大家分享一下。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
前兩天有在忙乎著關于解析XML文件的項目。呵呵,在這里跟大家分享一下。xml的解析無非就是對文件的分解,首先將每一個節(jié)點的標簽讀取出來,然后再讀節(jié)點中是否包含有參數(shù),如果存在參數(shù)的話則遍歷節(jié)點中的參數(shù),也就是分解"="兩邊的字符串,獲取左邊的作為參數(shù)名,而右邊的則為參數(shù)對應的值;再往下就判斷該節(jié)點他是否包含有"innerText",當然這里有要求,若當前節(jié)點以"/>"結尾,同時卻又包含"innerText",這種情況將視為語法錯誤;最后就是將節(jié)點下面的innerText解析成節(jié)點,如果存在節(jié)點的話。基本上XML解析就這點了,下面貼出部分我做的解析代碼,并作些解釋。
private synchronized Node parser(Node baseNode, String document) {
// 該段內容為空,不做任何解析
if (document == null) {
return null;
}
// 該方法為對子節(jié)點進行處理
if (document.indexOf('<') == -1 && document.indexOf('>') == -1) {
// 該段內容為innerText不做任何解析
return null;
}
if (document.indexOf('<') == -1 && document.indexOf('>') != -1) {
// 該段內容有誤,拋出異常
throw new XMLContentException();
}
if (document.indexOf('<') != -1 && document.indexOf('>') == -1) {
// 該段內容有誤,拋出異常
throw new XMLContentException();
}
if(document.indexOf("<!--") != -1 && document.indexOf("-->") == -1){
//該段內容有誤,拋出異常
throw new XMLContentException();
}
if(document.indexOf("<!--") == -1 && document.indexOf("-->") != -1){
//該段內容有誤,拋出異常
throw new XMLContentException();
}
// 用于匹配"<"后面出現(xiàn)的字符串是否為開始部分(第一個字母為英文字母),節(jié)點名稱長度不得小于1個字符
String regExTagStart = " *[A-Za-z][\\w.\\-:]+[\\da-zA-Z]+ *";
Pattern regexTagStart = Pattern.compile(regExTagStart);
document = document.substring(document.indexOf('<') + 1).trim();
// 如果當前節(jié)點不存在空格(比如<root>),則說明當前節(jié)點為"純節(jié)點節(jié)點"
// 用于截取節(jié)點名稱的索引
int endIndex = -1;
// 指示當前節(jié)點是否包含有參數(shù)
boolean hasParams = true;
if (document.substring(0, document.indexOf('>')).indexOf(' ') == -1) {
endIndex = document.indexOf('>');
hasParams = false;
} else {
endIndex = document.indexOf(' ');
}
// 獲取當前節(jié)點名稱
String tag = document.substring(0, endIndex);
// 此處加一些驗證
if (!regexTagStart.matcher(tag).matches()) {
// 如果驗證失敗,拋出異常
throw new XMLContentException();
}
// 創(chuàng)建用于存儲當前節(jié)點的節(jié)點對象
Node node = new Node(tag);
node.addLisener(nodeHandler);// 如果驗證通過,并且當前節(jié)點中包含參數(shù),則取出其節(jié)點中的參數(shù)及參數(shù)值
if (hasParams) {
document = document.substring(document.indexOf(' ')).trim();
// 獲取當前標簽行
String tagInline = document.substring(0, document.indexOf('>') + 1).trim();
if (tagInline.indexOf("/>") != -1
&& document.indexOf('>') == document.indexOf("/>") + 1) {
document = document.substring(document.indexOf("/>"));
} else if (tagInline.indexOf("/>") == -1) {
document = document.substring(document.indexOf('>'));
}
// 用于匹配標簽行
Pattern regExInline = Pattern
.compile("(\\w+ *= *\"[^\\n\\f\\r\"]*\"[\\n\\r\\t ]*)*/?>$");
if (!regExInline.matcher(tagInline).matches()) {
// 拋出異常
throw new XMLContentException();
}
// 遍歷節(jié)點所有屬性,并將其添加至節(jié)點集合中
while (true) {
// 如果當前節(jié)點中不存在參數(shù),跳出循環(huán)
if (tagInline.indexOf('=') == -1) {
break;
}
String paramName = new String();
String paramValue = new String();
boolean paramIsKeyword = false;
paramName = tagInline.substring(0, tagInline.indexOf('='))
.trim();
tagInline = tagInline.substring(tagInline.indexOf('=') + 1);
paramValue = tagInline.substring(tagInline.indexOf('"') + 1);
paramValue = paramValue.substring(0, paramValue.indexOf('"'));
tagInline = tagInline.substring(tagInline.indexOf('"')
+ paramValue.length() + 2);
// 如果節(jié)點參數(shù)名稱為關鍵名字如"name"和"value",則將其添加至特定的屬性當中
if (paramName.equalsIgnoreCase("name")) {
paramIsKeyword = true;
node.setName(paramValue);
}
if (paramName.equalsIgnoreCase("value")) {
paramIsKeyword = true;
node.setValue(paramValue);
}
// 當節(jié)點參數(shù)名稱不為關鍵名字則將其添加至參數(shù)集中
if (!paramIsKeyword) {
// 將參數(shù)添加至節(jié)點列表
node.addParam(paramName, paramValue);
}
}
}
// 如果當前節(jié)點以"/>"結尾,則忽略node對象的innerText值
if (document.indexOf("/>") != -1
&& document.indexOf('>') == document.indexOf("/>") + 1) {
// 當前節(jié)點已完畢,如果document中還存有文本,則繼續(xù)查找下一個節(jié)點
document = document.substring(document.indexOf('>') + 1);
if (document.length() > 0) {
baseNode.addNode(node);
node = parser(baseNode, document);
}
return node;
}
//獲取結束標簽,去掉標簽空格
document = document.replaceFirst("</[ ]*" + tag + "[ ]*>", "</" + tag + ">");
// 獲取當前節(jié)點的innerText值:此處有些許問題,假如</[tag]>中間包含有空格,則當前節(jié)點無法結束,導致語法錯誤
String innerText = document.substring(document.indexOf('>') + 1,
document.indexOf("</" + tag + ">"));
node.setInnerText(innerText);// 當前節(jié)點已完畢,如果document中還存有文本,則繼續(xù)查找下一個節(jié)點:此處同上
document = document.substring(
document.indexOf("</" + tag + ">") + tag.length() + 3).trim();
if (document.length() > 0) {
baseNode.addNode(node);
node = parser(baseNode, document);
}
return node;
}
代碼中基本上都是采用indexOf和substring來截取獲得標簽及其參數(shù),有些難看,不過我目前正準備將他改寫成以正則來解析,好了閑話不說多了,現(xiàn)在來分析他吧。
前面部分主要對xml文件(與其說xml文件,還不如說是參數(shù)baseNode的父節(jié)點的innerText)進行分析,如果xml文件出現(xiàn)語法錯誤,那么便立即停止對他的解析,并拋出異常;中間有這么一句:node.addLisener(nodeHandler);此句為向當前創(chuàng)建的節(jié)點對象添加一個監(jiān)聽器,這里很關鍵,在實現(xiàn)該解析器之前就了解到很多的xml解析都是一次從文件中加載所有文檔到內存中,然后再交給解析方法來遍歷文檔中所有的節(jié)點,并創(chuàng)建對應的對象,該例的特別之處就在于:他并不是讀取完xml文檔到內存中后遍歷所有的節(jié)點,而只是獲取其根節(jié)點,若有需要(即當用戶調用他的子節(jié)點的時候,因為首先需要獲得根節(jié)點下的節(jié)點集合,所以就可以在此處設置一個時間源,用來觸發(fā)解析當前節(jié)點的innerText),再去為他做解析,這樣若讀取了xml文檔并沒有完全用到其中的節(jié)點時,為系統(tǒng)提高了效率,而又因為這樣所以解析方法只需要負責當前父節(jié)點下innerText的解析,因此對系統(tǒng)的實現(xiàn)有很大的幫助,也便于理解程序。下面還有一段代碼也非常關鍵,做完后足足為此調試了一下午。舉個例子,若當前父節(jié)點下面存在多個并列子節(jié)點,而方法只能返回一個節(jié)點對象,若遍歷所有節(jié)點必然覆蓋先前的節(jié)點,后來就想個辦法在方法里面再加了一個參數(shù),即baseNode,他是調用該解析方法是傳入的父節(jié)點對象,如果存在多個節(jié)點對象,那么就將其添加至baseNode中,因為類對象是存在于堆中,因此對象不會被覆蓋,只會在原來的對象上做修改。
下面是有關當節(jié)點被調用的時候觸發(fā)時間處理
@Override
public void innerTextUsing(Node node, String innerText) {
// TODO Auto-generated method stub
Node n = parser(node, innerText);
if (n == null)
return;
node.addNode(n);
}
此處代碼很簡單,事件觸發(fā)的時候事件源會傳入觸發(fā)事件的節(jié)點對象及他的innerText值,然后直接交給解析方法處理就可以了。
結語:整個解析過程很簡單,文檔對象創(chuàng)建之后會自動調用一個預處理方法,該方法主要對xml文件頭進行處理及去掉文檔中的注釋,然后再執(zhí)行解析方法,解析方法第一次工作只解析xml文檔根節(jié)點,然后將剩余的內容存至根節(jié)點的innerText,當調用節(jié)點的獲取節(jié)點方法時便觸發(fā)一個事件,然后事件里面就將節(jié)點對應的innerText進行解析,如此循環(huán)。
本站文章除注明轉載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自:網(wǎng)絡轉載