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