分享
 
 
 

XML文件处理的思考[2004年5月11日 10:18]

王朝vc·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

1。用分隔符隔开的字符串表示节点路径信息(BCB)。思考原因,一些简单的参数,如果嵌

套的比较深入的话,如果用一般XML处理的方法,逐步深入,需要定义好些变量,太麻烦。

假设XML文件为

<XNETCONFIG>

<LOCALNAME>ohahu</LOCALNAME>

<PORT>6800</PORT>

<MAXCLIENT>50</MAXCLIENT>

</XNETCONFIG>

访问XNetConfig的Port子节点的方法需要先获取XNetConfig节点,然后再获取Port节点。

下面通过通过字符串“XNetConfig.Port”来直接获取Port节点。

函数实现如下:

//定义一个回调函数模板。这样,对于所有找节点的操作就不用关心,只要关心你要对这

个节点做什么

//函数 返回值类型bool *表示是指针 ActionProc名称 参数_di_IXMLNode pNode,void *

pValue(void *pValue为自适应)

typedef bool (*ActionProc)(_di_IXMLNode pNode,void *pValue);

//AnsiString和CString是类似的

bool RetrieveNodeByDir(_di_IXMLNode pParent,AnsiString strList,void *pValue,Ac

tionProc doit)

{

try

{

_di_IXMLNodeList ChildList = pParent->ChildNodes;

if(ChildList==NULL||strList==NULL)

return false;//节点不存在,自然处理就不成功了

else

{

_di_IXMLNode ChildNode;

int nLen = strList.AnsiPos(".")>0?strList.AnsiPos(".")-1:strList.L

ength();

AnsiString strCur = strList.SubString(1,nLen);//这个地方测试不够充

if((ChildNode = ChildList->FindNode(WideString(strCur)))!=NULL)

{

if(strCur==strList)

{

return doit(ChildNode,pValue);//调用处理函数

}

else

{

int nStart = strList.AnsiPos(".")>0?strList.AnsiPos(".")+1

:1;

strCur = strList.SubString(nStart,strList.Length());

if(GetXMLMinNodeByDir(ChildNode,strCur,pValue,doit))//递归

调用,处理子节点

return true;

}

}

}

}

catch(...)

{

return false;

}

return false;

}

这个函数,可以通过ActionProc来执行设置节点值、属性,获取节点值、属性,等操作。

后面有ActionProc的范例

////----------------------------------------------------------------------------------------------

2。根据节点名称来查找节点

如上面的Port节点,只需要一个”Port”作为参数,而不需要完整路径。当然,这样如果

整个文档有多个Port的时候,将不能识别,但是可以经过修改,使这个函数能支持查找所

有符合条件的节点。如果能和上面一样加入回调函数更好。

函数如下:

bool GetTextIntByTag(_di_IXMLNode pParent,AnsiString strTag,int &nValue)

{

//bool bSuccess = false;

_di_IXMLNodeList ChildList = pParent->ChildNodes;

if(ChildList==NULL||strTag==NULL)

return false;

else

{

_di_IXMLNode ChildNode;

if((ChildNode = ChildList->FindNode(WideString(strTag)))!=0&&

ChildNode->ChildNodes!=NULL&&ChildNode->ChildNodes->Count==1)

{

int nOldValue = nValue;

try

{

AnsiString str;

str = ChildNode->Text;

nValue = str.ToInt();

return true;

}

catch(...)

{

nValue = nOldValue;

}

}

else

{

for(int i=0;iCount;i++)

{

ChildNode = ChildList->GetNode(i);

if(GetXMLTextIntByTag(ChildNode,strTag,nValue))

return true;

}

}

}

return false;

}

////----------------------------------------------------------------------------------------------

3。从上面可以看出,在遍历的函数中使用回调函数是一个非常好的选择。当然要定义一个

良好的回调函数,这个就是C++的优势,比如在上面的void指针,我们可以把void指针随便

转换成需要的类型。指针的转换,除了下面的直接转换以外,还有dynamic_cast,static

_cast,const_cast,其中static_cast用的比较多,具体的还是自己找资料去。做程序,少

不了的就是找资料。

第一个函数的回调函数示例:

//获取节点属性

bool GetNodeStr(_di_IXMLNode pNode,void *pValue)

{

AnsiString strOldValue = *(AnsiString *)pValue;//转换成字符串指针,然后取得

这个对象

try

{//把节点的信息放到pValue中,下面这样赋值个人觉得是没有什么问题,因为在外面

定义的是AnsiString,然后这里按照AnsiString的规则来处理它,当然如果外面定义pValu

e是一个指向int的指针,我就不知道会发生什么事了

*(AnsiString*)pValue = pNode->Text;

return true;

}

catch(...)

{

*(AnsiString*)pValue = strOldValue;

}

return false;

}

//设置节点属性

bool SetNodeStr(_di_IXMLNode pNode,void *pValue)

{

try

{

//设置节点的值,这里的_di_IXMLNode是COM接口,所以这里要用WideString

pNode->Text = WideString(*(AnsiString*)pValue);

return true;

}

catch(...)

{

return false;

}

}

使用示例:

//DocNode是指向XML文档节点的接口(指针??),对于上面来说就是XNetConfig节点

int nValue = Left;

RetrieveNodeByDir(DocNode,AnsiString("MainForm.Left"),(void*)&nValue,SetNodeI

nt);

if(RetrieveNodeByDir(DocNode,AnsiString("MainForm.Left"),(void*)&nValue,GetNod

eInt))

Left = nValue;

用AnsiString的例子

AnsiString strValue = Caption;

RetrieveNodeByDir(DocNode,AnsiString("MainForm.Caption"),(void*)&strValue,Set

NodeStr);

if(RetrieveNodeByDir(DocNode,AnsiString("MainForm.Caption"),(void*)&strValue,G

etNodeStr))

Caption = strValue;

也许有人不明白用回调函数的意义是什么,顺便啰嗦两句。

一个比较浅显的应用是把上面1(包括typedef),其实可以把它封装到dll文件中(这里不考虑线程问题),这样在Exe中动态调用(LoadLibrary,GetProcAddress)的时候,只要指定一个实现的函数,比如上面的设值,读值等,就可以使用了,完全不用关心,如何去按照“xxx.aa”这样的路径怎么找到等实现细节。而且,对于实现不同的功能,这样检索路径的代码,只需要写一次就行了。需要注意的是,ActionProc必须是一个固定的结构(参数,返回值),可能是不同的数据类型,但占的字节数,必须一样。

////----------------------------------------------------------------------------------------------

4.上面提到_di_IXMLNode是一个接口,Com的调用多有一个差不多的规律,当然,在VC中有不同的实现过程,这里说一下我觉得有必要说的XML的Com调用的部分内容

#import "C:\\Windows\\system32\\MSXML.DLL" named_guids

在stdafx.h里面加入这句,引入XML调用,相关可以看

//初识Com的应用总结

//http://bbs.hziee.edu.cn/bbscon.php?board=vc&id=239

在微软的许多COM中常常出现的get__newEnum的使用,懂得了他的使用方法,也就知道了怎么遍历

具体也不说很多了,先把遍历节点的代码贴出来,因为,自己对COM的机理也没有很大的把握,下面说错了,可不要扔石头啊,呵呵

//还是一个回调函数

typedef void (*EnumNodeProc)(MSXML::IXMLDOMNodePtr pElement);

INT CXmlFile::EnumNode(MSXML::IXMLDOMNodeListPtr pNodeList, EnumNodeProc EnumProc)

{

if(pNodeList==NULL)

return 0;

MSXML::IXMLDOMNodePtr pNode;

CComPtr spDispatch;

IUnknownPtr pUnk;

int nCnt = 0;

try

{

HRESULT hr = pNodeList->get__newEnum(&pUnk);//这里生成了pUnk对COM的接口调用

if(FAILED(hr))

{

return nCnt;

}

CComPtr pEnum;

//但是pUnk还不是Enum接口,但是Enum接口已经包含在pUnk里面了,

//在这里pUnk是不是IUnknownPtr接口并不重要,重要的是它要包含IEnum接口

//但是,前面get_newEnum需要是IUnknown接口。

hr = pUnk->QueryInterface(IID_IEnumVARIANT,(VOID**)&pEnum);//生成IEnum调用

if(pEnum)

{

pEnum->Reset();

ULONG fget = 1;

while(SUCCEEDED(hr)&&fget>0)

{

_variant_t varDisp;

hr = pEnum->Next(1,&varDisp,&fget);//下一个记录

if(SUCCEEDED(hr) && fget>0)

{

pNode = varDisp.pdispVal;

nCnt++;

EnumProc(pNode);//调用回调函数

pNode = NULL;

}

}

pEnum.Release();//有调用就需要释放,COM的记数准确,才能在没有调用的时候自动释放

}

pUnk.Release();

}

catch(_com_error &e)

{

;

}

return nCnt;

}

上面关于get_newEnum的用法,来自CSDN论坛VC版老大哥masterz的指导,另外还有一个成员_newEnum的使用,我也没有试出来,参考上面的方法,老是出错,如果有哪位试出来了,麻烦告知。

COM的处理流程在《vc技术内幕 第五版》的电子版第四章(到网上找一下)中有较好的介绍。

6月26日 2:43补:关键字:字符串定位XML节点,XML插入节点

竟然发现MSXML中本身就带了用字符串查找节点的函数,汗

参考: http://www.vcer.net/showTip.jsp?tipid=2248

使用方法如下:

MSXML::IXMLDOMDocumentPtr m_pDoc;

MSXML::IXMLDOMNodePtr NodePtr=NULL;

if((NodePtr=m_pDoc->selectSingleNode(_bstr_t("distributeservice/mainservice")))!=NULL)

printf("Find distributeservice/mainservice\n");

再有需要添加节点都是由XMLDocument创建出来,再由子节点插入的,如

MSXML::IXMLDOMDocumentPtr pDoc;

MSXML::IXMLDOMElementPtr pElem = NULL;

pDoc.CreateInstance(__uuidof(MSXML::DOMDocument));

pDoc->loadXML(_bstr_t("<test>hello</test>"));

pElem = pDoc->GetdocumentElement();

if(pElem!=NULL)

{

_bstr_t tmp = pElem->Getxml();

pElement->appendChild((MSXML::IXMLDOMNodePtr)pElem);//发现没有,这样也行

}

pDoc.Release();

pDoc=NULL;

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有