辛懷聲 王鵬
摘要:在開展數(shù)據(jù)處理和數(shù)據(jù)分析的工作前,常常需要對采集的原始數(shù)據(jù)進行解析處理,常規(guī)的方法是針對大量的數(shù)據(jù)接口分別開發(fā)解析代碼,這樣做不僅工作量大,代碼冗余度高,日后對接口進行修改或添加時都需要同步修改解析程序的相應代碼,給數(shù)據(jù)分析處理工作帶來極大的困難。為了有效解決這一問題,該文提出了一種基于XML的數(shù)據(jù)解析方法,實現(xiàn)接口定義和程序編碼的隔離,能夠在不改動程序代碼情況下,實現(xiàn)數(shù)據(jù)接口的按需添加或修改,有利于節(jié)約后期針對解析程序的開發(fā)和維護成本。
關(guān)鍵詞:XML;數(shù)據(jù)解析;隔離;接口
中圖分類號:TP319 文獻標識碼:A 文章編號:1009-3044(2014)01-0057-03
1 概述
在日常軟件工程中為了優(yōu)化或者增加新功能往往需要對數(shù)據(jù)接口進行修改或刪減。這時數(shù)據(jù)記錄設(shè)備記錄下來的數(shù)據(jù)格式也會隨之發(fā)生改變。對于事后的數(shù)據(jù)分析處理工作來說,這意味著數(shù)據(jù)解析的開發(fā)工作會伴隨著數(shù)據(jù)接口的改變而一直進行。經(jīng)常出現(xiàn)的一種情況就是進行數(shù)據(jù)解析時程序報錯,進行錯誤定位之后發(fā)現(xiàn)是數(shù)據(jù)格式發(fā)生了變化,于是需要對相應的數(shù)據(jù)接口進行代碼開發(fā)或更改,這給數(shù)據(jù)解析工作帶了很大的不便,降低了數(shù)據(jù)分析的效率,也加大了數(shù)據(jù)解析和分析出錯的幾率。
2 解決辦法
可擴展標記語言(Extensible Markup Language, XML)是一種用于描述數(shù)據(jù)與平臺無關(guān)的語言,以一種開放的自我描述方式定義數(shù)據(jù)結(jié)構(gòu),在描述數(shù)據(jù)內(nèi)容的同時突出對結(jié)構(gòu)的描述,是一種存儲結(jié)構(gòu)化數(shù)據(jù)的規(guī)范[1-2]。XML是一種元標記語言, 可以用來定義其他的標記語言, 并且這些標記語言的元素標記是由用戶自己定義的。所以由XML可以派生出無限多種標記語言。在這些標記語言必須根據(jù)一定得規(guī)則來定義和組織,但是這些標記在其含義上是非常靈活的[3]。
本文使用XML對接口數(shù)據(jù)進行映射,不在程序代碼中對數(shù)據(jù)接口進行“硬編碼”,之后針對XML生成文件解析指令序列,最后按照指令序列對數(shù)據(jù)文件進行解析,把解析程序代碼與接口定義進行隔離,從而使數(shù)據(jù)接口的更改對解析程序代碼的影響降到最低。
3 程序設(shè)計
3.1 XML與數(shù)據(jù)結(jié)構(gòu)映射
待解析的數(shù)據(jù)都是由網(wǎng)絡(luò)報文記錄而成的二進制文件,報文內(nèi)容如圖1所示。
如圖1所示,最前面的數(shù)據(jù)是消息代碼,用來標識消息類型,之后是消息長度,用來標識消息之間的邊界位置。通過這兩個值我們可以識別和解析數(shù)據(jù)文件中的所有二進制數(shù)據(jù)的內(nèi)容。對于上述數(shù)據(jù),我們可以用數(shù)據(jù)結(jié)構(gòu)(struct)或類(class)來進行抽象。如下面C++代碼所示:
struct Header
{int messagetype;
int length;
char spare[4]
};
struct DataTypeA
{Header head;
int A;
int B
};
接口數(shù)據(jù)類型DataTypeA包括了消息頭結(jié)構(gòu)和消息體數(shù)據(jù)A和B。消息頭包括了消息類型消息長度和一些其他數(shù)據(jù)以及保留數(shù)據(jù)空間。
進一步可以用XML對其進行表示,如下所示:
<?xml version="1.0" encoding="utf-8"?>
上面的XML中ID表示數(shù)據(jù)項名稱。Array表示是否為數(shù)組,如果Array的值是1,則表示單個數(shù)據(jù)項,如果大于1,則表示一個數(shù)組。Style表示是否為嵌套的數(shù)據(jù)結(jié)構(gòu),如果值為“element”表示不是數(shù)據(jù)結(jié)構(gòu),如果值為“structure”則表示是數(shù)據(jù)結(jié)構(gòu),需要從XML中另行查找它的具體定義。 從上面XML中可以看出,這里定義了兩類數(shù)據(jù)結(jié)構(gòu),一類是HEADER數(shù)據(jù)結(jié)構(gòu),另一類是DataTypeA數(shù)據(jù)結(jié)構(gòu),而DataTypeA數(shù)據(jù)結(jié)構(gòu)又嵌套了HEADER數(shù)據(jù)結(jié)構(gòu)。這與前面的C++代碼是一致的。通過上面的步驟,我們建立了一種二進制接口數(shù)據(jù)到XML的映射。
3.2 文件解析指令序列
對數(shù)據(jù)接口進行XML映射之后,程序代碼就可以與具體的數(shù)據(jù)接口定義進行隔離了,從而實現(xiàn)數(shù)據(jù)接口變化不影響程序代碼的目標。為了實現(xiàn)程序代碼與數(shù)據(jù)接口隔離,程序?qū)⒏鶕?jù)XML文件的內(nèi)容生成一系列的數(shù)據(jù)解析指令。 這里的指令序列指的是由一系列文件讀取的長度和數(shù)據(jù)類型組成的序列,如下表1所示:
表1 數(shù)據(jù)解析指令序列
[接口類型\&數(shù)據(jù)名稱\&數(shù)據(jù)類型\&DataTypeA\&MESSAGE_TYPE\∫\&MESSAGE_LEN\∫\&MESSAGE_SPARE\&char\&MESSAGE_SPARE\&char\&MESSAGE_SPARE \&char\&MESSAGE_SPARE\&char\&A\∫\&B\∫\&]
之后我們就可以根據(jù)上述指令序列對待解析的文件進行解析讀取。
3.3 XML文件處理流程
為了生成文件解析指令序列,我們需要對XML文件進行處理。XML文件的處理流程如下圖2所示,首先讀取DataTypeA對應的XML文件,然后定位到定義DataTypeA的節(jié)點,之后循環(huán)處理DataTypeA的子節(jié)點。處理過程是:如果該子節(jié)點為簡單數(shù)據(jù)類型,則將該節(jié)點中的數(shù)據(jù)類型信息和數(shù)據(jù)名稱加入解析指令序列;如果該節(jié)點表示的是一個數(shù)據(jù)結(jié)構(gòu)則啟動遞歸過程找到定義該數(shù)據(jù)類型的節(jié)點,并重復與DataTypeA相同的處理過程,直到處理完所有DataTypeA的子節(jié)點為止。
3.4 文件數(shù)據(jù)解析流程
生成了接口數(shù)據(jù)類型對應的文件解析指令序列后,我們可以根據(jù)文件解析指令序列對文件進行解析。順序讀取解析指令,根據(jù)數(shù)據(jù)類型決定調(diào)用的文件讀取函數(shù)。例如:如果當前一條文件讀取指令指示的數(shù)據(jù)類型為char,我們就可以調(diào)用相應的類似ReadChar()之類的函數(shù)(由用戶的變成語言確定);如果指示的數(shù)據(jù)類型為float,我們就可以調(diào)用類似ReadFloat()之類的函數(shù)。流程如圖3所示。
4 結(jié)束語
由于大多數(shù)編程語言都對XML處理提供了強大的處理函數(shù),該文介紹的利用XML文件映射接口數(shù)據(jù),之后再解析數(shù)據(jù)的方法可以用于大部分編程語言環(huán)境。本方法使程序代碼與接口定義解除耦合關(guān)系,做到了新定義接口或修改后的接口的“即插即用”,無需針對接口的修改對解析程序進行修改和重新編譯,提高了數(shù)據(jù)解析的工作速度和正確率。
參考文獻:
[1] 甘小斌.XML標準體系介紹[J].信息技術(shù)與標準化,2004(9): 41-44.
[2] 陳春詠.基于XML的指揮自動化輔助決策系統(tǒng)研究[D].南京:東南大學,2007.
[3] 懷石工作室.XML完全手冊[M].北京:中國電力出版社,2000.