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