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