vba xml 怎么设置父节点_熊二做了一个xml报文处理的开源库easyxml
自信、冷靜、專注。—— TM 熊的自我勉勵(lì)
?1. 前言
熊二從去年開始,因項(xiàng)目需求接觸到xml報(bào)文的處理,也是我第一次學(xué)習(xí)用C/C++的方式處理基于DOM模型的xml報(bào)文。因?yàn)楸救吮容^懶hhh,所以第一反應(yīng)就是去看看網(wǎng)上有沒有什么大家都在用的比較好的xml處理開源庫(kù),站在前輩的肩膀上才能看的更遠(yuǎn)嘛。
2. 我找到了哪些庫(kù)
| Tinyxml2 | C++ | https://github.com/leethomason/tinyxml2 | DOM |
| libxml2 | C | http://xmlsoft.org/sources/win32/libxml2-2.7.8.win32.zip | DOM |
| CMarkup | C++ | http://www.firstobject.com/Markup115.zip | DOM |
| Mini-XML | C | http://www.msweet.org/files/project3/mxml-2.9.tar.gz | DOM/SAX |
| Expat-XML | C | https://github.com/libexpat/libexpat/ | SAX |
| Xerces | C++/Java/Perl | http://xerces.apache.org/mirrors.cgi | DOM/SAX |
- SAX(Simple API for XML)是基于事件的,其基本工作流程是分析XML文檔,當(dāng)發(fā)現(xiàn)了一個(gè)新的元素時(shí),產(chǎn)生一個(gè)對(duì)應(yīng)事件,并調(diào)用相應(yīng)的用戶處理函數(shù)。這種方式占用內(nèi)存少,速度快,但用戶程序相應(yīng)地會(huì)比較復(fù)雜。
- DOM(Document Object Model)是在分析時(shí),一次性地將整個(gè)XML文檔進(jìn)行分析,并在內(nèi)存中形成對(duì)應(yīng)的樹結(jié)構(gòu),同時(shí),向用戶提供一系列的接口來訪問和編輯該樹結(jié)構(gòu)。這種方式占用內(nèi)存大,速度往往慢于SAX,但可以給用戶提供一個(gè)面向?qū)ο蟮脑L問接口,對(duì)用戶更為友好。
最后我選擇了tinyxml2這個(gè)庫(kù)作為項(xiàng)目使用,但關(guān)于這個(gè)庫(kù),網(wǎng)上的資料看了很多總覺得不算太理想。于是我嘗試僅從使用者地角度去看看這個(gè)庫(kù)有沒有可能再封裝一次,使其處理xml報(bào)文變得更簡(jiǎn)單?
?3. 我用到了tinyxml庫(kù)的哪些功能
- 3.1. 根據(jù) DOM模型,XML 文檔中的每個(gè)成分都是一個(gè)節(jié)點(diǎn)。整個(gè)文檔是一個(gè)文檔節(jié)點(diǎn),每一個(gè)xml元素是一個(gè)元素節(jié)點(diǎn),而包含在xml元素中的文本是一個(gè)文本節(jié)點(diǎn),每一個(gè)xml屬性是一個(gè)屬性節(jié)點(diǎn),總之,基于DOM模型,xml文檔皆為節(jié)點(diǎn)。tinyxml2正好利用了這一特性,以節(jié)點(diǎn)的概念來設(shè)計(jì)處理函數(shù)。
- 3.2. ?xml報(bào)文的處理在廣義上來說,無非“增”、“刪”、“改”、“查”四部分,我在處理xml報(bào)文時(shí),用的最多的就是“增”、“查”,即協(xié)議交互中報(bào)文的組裝和解析。通過將近半年的使用體驗(yàn),不得不說,真的很感謝tinyxml2的作者Lee Thomason先生,能創(chuàng)作出這個(gè)好用的庫(kù)并開源。接下來我會(huì)展示tinyxml組裝和解析的步驟,并嘗試優(yōu)化我覺得不合理的地方。
4. xml報(bào)文的組裝
比如我們要組裝這樣一段xml報(bào)文
<?xml version="1.0" encoding="UTF-8"?>"south-bear">
0.0.0.0
8888
bear-2
這是用tinyxml2的基本接口組裝報(bào)文的方法
XMLDocument xmlDoc;XMLNode* parent = NULL;
XMLElement *child[16] = { NULL };
parent->InsertEndChild( xmlDoc.NewDeclaration("xml version=\"1.0\" encoding=\"UTF-8\""));
child[1]=xmlDoc.NewElement("param"); //創(chuàng)建一個(gè)名為param的節(jié)點(diǎn)
child[1]->SetAttribute("name","south-bear"); //設(shè)置該節(jié)點(diǎn)屬性
parent->InsertEndChild(child[1]); //將該節(jié)點(diǎn)加入到parent節(jié)點(diǎn)下
child[2]=xmlDoc.NewElement("ipAddress"); //創(chuàng)建一個(gè)名為ipAddress的節(jié)點(diǎn)
child[2]->SetText("0.0.0.0"); //設(shè)置該節(jié)點(diǎn)內(nèi)容
child[1]->InsertEndChild(child[2]); //將該節(jié)點(diǎn)加入到param節(jié)點(diǎn)下
child[2]=xmlDoc.NewElement("portNo"); //創(chuàng)建一個(gè)名為portNo的節(jié)點(diǎn)
child[2]->SetText(8888); //設(shè)置該節(jié)點(diǎn)內(nèi)容
child[1]->InsertEndChild(child[2]); //將該節(jié)點(diǎn)加入到param節(jié)點(diǎn)下
child[2]=xmlDoc.NewElement("channel"); //創(chuàng)建一個(gè)名為channel的節(jié)點(diǎn)
child[1]->InsertEndChild(child[2]); //將該節(jié)點(diǎn)加入到param節(jié)點(diǎn)下
child[3]=xmlDoc.NewElement("Name"); //創(chuàng)建一個(gè)名為Name的節(jié)點(diǎn)
child[3]->SetText("bear-2"); //設(shè)置該節(jié)點(diǎn)內(nèi)容
child[2]->InsertEndChild(child[3]); //將該節(jié)點(diǎn)加入到channel節(jié)點(diǎn)下
怎么樣?第一眼看上去是不是有點(diǎn)懵?感覺很不好記?如果是就對(duì)了,我當(dāng)時(shí)第一反應(yīng)也是這樣。tinyxml2庫(kù)在插入元素節(jié)點(diǎn)時(shí)都會(huì)用到NewElement和InsertEndChild等接口,但是每插入一個(gè)節(jié)點(diǎn)后,想在該節(jié)點(diǎn)下插入下一級(jí)節(jié)點(diǎn)就很容易將節(jié)點(diǎn)搞混淆,就是說,你在使用tinyxml2庫(kù)這個(gè)接口時(shí),必須將每一個(gè)插入的節(jié)點(diǎn)記下,否者可能導(dǎo)致組裝的xml報(bào)文就是混亂的,另外從篇幅上來看重復(fù)的代碼還是占太多了。
于是我開始設(shè)想,有沒有這樣一種庫(kù),我只需要傳入每一級(jí)節(jié)點(diǎn)的相關(guān)參數(shù),就能自動(dòng)組裝xml報(bào)文,下面是我設(shè)想的組裝xml的代碼。
XMLDocument xmlDoc;XMLNode* parent = NULL;
XMLElement *child[16] = { NULL };
child[1]=xmlAddNode(xmlDoc, parent, "param");//創(chuàng)建一個(gè)名為param的節(jié)點(diǎn),將該節(jié)點(diǎn)加入到parent節(jié)點(diǎn)下
xmlSetNodeAttr(child[1], "name", "south-bear");//設(shè)置該節(jié)點(diǎn)屬性
xmlAddNode(xmlDoc,child[1], "ipAddress", "0.0.0.0");//在param節(jié)點(diǎn)下添加ipAddress節(jié)點(diǎn)
xmlAddNode(xmlDoc,child[1], "portNo", 8888);//在param節(jié)點(diǎn)下添加portNo節(jié)點(diǎn)
child[2]=xmlAddNode(xmlDoc, child[1], "channel");//在param節(jié)點(diǎn)下添加channel節(jié)點(diǎn)
xmlAddNode(xmlDoc, child[2], "Name", "bear-2");//在channel節(jié)點(diǎn)下添加Name節(jié)點(diǎn)
xmlAddNode和xmlSetNodeAttr接口是我在tinyxml2庫(kù)的基礎(chǔ)上封裝的庫(kù)。
?對(duì)于xmlAddNode接口,你需要傳入
?- xml文檔對(duì)象
- 想要?jiǎng)?chuàng)建的節(jié)點(diǎn)的父節(jié)點(diǎn)
- 節(jié)點(diǎn)名稱
- 該節(jié)點(diǎn)的文本內(nèi)容(可選)
對(duì)于xmlSetNodeAttr接口,你需要傳入
?- 需要設(shè)置屬性的節(jié)點(diǎn)
- 屬性名稱
- 屬性內(nèi)容 是不是容易理解多了,而且光看child數(shù)組元素的下標(biāo)就能知道當(dāng)前處在哪一級(jí)。操作起來也更方便
5. xml報(bào)文的解析
這是用tinyxml2的基本接口解析上述報(bào)文的方法
XMLDocument xmlDoc;XMLElement *pXmlRoot = NULL;
XMLElement *pXmlTra = NULL;
char name[32] = {0};
char ipAddress[32] = {0};
int port = 0;
if(0 == xmlDoc.Parse((const char *)xmlBuf))
{
pXmlRoot = xmlDoc.RootElement();
pXmlTra = pXmlRoot->FirstChildElement("ipAddress");
if (pXmlTra != NULL)
{
strncpy(ipAddress, (pXmlTra->GetText()), (sizeof(ipAddress)-1));
}
pXmlTra = pXmlRoot->FirstChildElement("portNo");
if (pXmlTra != NULL)
{
sscanf(pXmlTra->GetText(), "%d", &port);
}
pXmlTra = pXmlRoot->FirstChildElement("channel");
pXmlTra = pXmlTra->FirstChildElement("Name");
if (pXmlTra != NULL)
{
strncpy(name, (pXmlTra->GetText()), (sizeof(name)-1));
}
}
emmmm....看上去還是很繁瑣,能不能變得簡(jiǎn)潔點(diǎn)?下面是我設(shè)想的解析xml報(bào)文的代碼,
XMLDocument xmlDoc;XMLElement *pXmlRoot = NULL;
XMLElement *pXmlTra = NULL;
char name[32] = {0};
char ipAddress[32] = {0};
int port = 0;
if(0 == xmlDoc.Parse((const char *)xmlBuf))
{
pXmlRoot = xmlDoc.RootElement();
xmlGetNodeAttr(pXmlRoot, "name", name, sizeof(name));
xmlGetChildNode(pXmlRoot, "ipAddress", ipAddress, sizeof(ipAddress));
xmlGetChildNode(pXmlRoot, "portNo", &port);
xmlGetChildNode(pXmlRoot->FirstChildElement("channel"),"Name", channelName, sizeof(channelName));
}
xmlGetNodeAttr和xmlGetChildNode接口是我在tinyxml2庫(kù)的基礎(chǔ)上封裝的庫(kù)。
?對(duì)于xmlGetNodeAttr接口,你需要傳入
?- 節(jié)點(diǎn)指針
- 屬性名稱
- 存放屬性內(nèi)容的緩沖區(qū)
對(duì)于xmlGetChildNode接口,你需要傳入
?- 父節(jié)點(diǎn)
- 想要獲取的節(jié)點(diǎn)名稱
- 存放該節(jié)點(diǎn)內(nèi)容的緩沖區(qū)
6.easyxml庫(kù)
根據(jù)上面的設(shè)想,我基于tinyxml2庫(kù)開發(fā)了easyxml庫(kù),這是源碼目錄樹
├── demo│?? ├── create.cpp
│?? ├── parser.cpp
│?? └── test.xml
├── src
│?? ├── easyxml.cpp
│?? ├── easyxml.hpp
│?? └── xml_cofig.h
└── third
├── tinyxml2.cpp
└── tinyxml2.h
目錄樹包含了依賴的tinyxml2源碼和使用demo, 在src目錄下的即為easyxml庫(kù)的源碼,easyxml庫(kù)的接口有兩個(gè)實(shí)現(xiàn)版本,用C++函數(shù)和宏來實(shí)現(xiàn),兩種版本各有優(yōu)缺點(diǎn),在使用中我們按照需求場(chǎng)景來選擇。「C++函數(shù)接口」:
- 優(yōu)點(diǎn):利用c++函數(shù)的可重載性,解析各種類型的字段時(shí),接口名稱可以不變。
- 缺點(diǎn):因?yàn)槭呛瘮?shù)接口,存在實(shí)參轉(zhuǎn)形參的過程。比如當(dāng)獲取字符串類型的字段時(shí),為防止溢出,我們需要傳入緩沖區(qū)的長(zhǎng)度,這樣一來函數(shù)的入?yún)⒕鸵黾右粋€(gè),觀感上就會(huì)不整潔。
「宏接口」:
- 優(yōu)點(diǎn):因不存在存在實(shí)參轉(zhuǎn)形參的過程,完美解決了C++函數(shù)接口的缺點(diǎn),觀感整潔。
- 缺點(diǎn):因宏不具備可重載性,所以解析各種類型的字段時(shí),就要設(shè)計(jì)該種類型對(duì)應(yīng)的宏接口
如上面目錄所示,easyxml.hpp存放的是C++函數(shù)接口,xml_cofig.h存放的是宏接口 總結(jié):如果是對(duì)于少量代碼的工程,上面兩種版本的選擇沒有太大的差別,但對(duì)于大量代碼的工程,推薦選擇宏接口。
?「easyxml庫(kù)的開源地址」https://github.com/southbear-club/easyxml
?其中包含的接口當(dāng)然不止上面示例代碼中的這些庫(kù)啦,當(dāng)然一些更原子的操作還是要用tinyxnl2的基本接口的,不過easyxml庫(kù)已經(jīng)能幫你解決大部分的xml報(bào)文處理的需求了,這個(gè)庫(kù)后續(xù)我會(huì)持續(xù)維護(hù)更新,以使得其能應(yīng)用于更多的場(chǎng)景。歡迎star喲~
?7. 關(guān)于tinyxml2
「?jìng)魉烷T」如果在這之前,你還不太了解xml報(bào)文的基礎(chǔ)知識(shí),可以訪問下面的鏈接惡補(bǔ)惡補(bǔ)。
?https://www.runoob.com/dom/dom-intro.html
?tinyxml2庫(kù)的接口使用說明
?http://leethomason.github.io/tinyxml2/annotated.html
?「我遇到了哪些坑」
- XMLDocument類中的Parse函數(shù),可以選擇傳入"len"參數(shù),即要解析的字節(jié)數(shù)。如果未指定,TinyXML-2將假定'xml'指向以NULL終止的字符串。
- XMLPrinter類中的CStrSize()函數(shù)用以獲取xml報(bào)文的總長(zhǎng)度,但返回值長(zhǎng)度包含終止符null,所以用CStrSize()函數(shù)獲取xml的長(zhǎng)度比實(shí)際值多1。
- 如果你希望生成的xml報(bào)文是不帶格式對(duì)齊的,那只需要在定義XMLPrinter類的對(duì)象時(shí)指定compact值為true即可,例如:?
XMLPrinter printer(0,true,0);
?
暫時(shí)就想到了這些坑,真是一把心酸淚,希望你們不要再去踩這些坑了。
8.嘮嘮叨叨
最近杭州突然變得好冷啊,大家記得添衣服別感冒呀,感冒了干啥都不開心。好啦,以上就是本期的全部?jī)?nèi)容,學(xué)習(xí)不是為了變得全知全能,而是為了不再害怕未知,我是熊二,我們下期再見。
推薦閱讀:
從cmockery入門C語言單元測(cè)試
你的github開源項(xiàng)目還沒做持續(xù)集成么?趕緊看看這個(gè)
還在為查內(nèi)存泄漏問題痛苦不堪?試試這個(gè)神器
?????????????????????????? ? ? ??
????????????????????????????????????
總結(jié)
以上是生活随笔為你收集整理的vba xml 怎么设置父节点_熊二做了一个xml报文处理的开源库easyxml的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Xmind模板文档分享——学习记录(4)
- 下一篇: Viewpager无限循环(首页与尾页平