XML文件处理的思考

  首 页 | 新 闻 | 技术中心 | 第二书店 | 《程序员》 | 《开发高手》 | 社 区 | 黄 页 | 人 才
移 动专 题SUNIBM微 软微 创精 华Donews人 邮
我的技术中心 
我的分类我的文档
全部文章发表文章
专栏管理使用说明

 RSS 订阅 
最新文档列表
Windows/.NET
.NET  (rss)    
Visual C++  (rss)    
Delphi  (rss)    
Visual Basic  (rss)    
ASP  (rss)    
JavaScript  (rss)    
Java/Linux
Java  (rss)    
Perl  (rss)    
综合
其他开发语言  (rss)    
文件格式  (rss)    
企业开发
游戏开发  (rss)    
网站制作技术  (rss)    
数据库
数据库开发  (rss)    
软件工程
其他  (rss)    

积极原创作者 
A8P8D3JK (1)
adamsun (3)
ahxu (2)
maoerzuozuo (6)
playyuer (30)
lgchao (2)
devercn (8)
foxmail (3)
netcasper (2)
cker (12)
CSDN - 文档中心 - Visual C++ 阅读:1762   评论: 0    参与评论
标题  XML文件处理的思考[2004年5月11日 10:18]     hahu [原作]
关键字  XML文件处理的思考[2004年5月11日 10:18]
出处 

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;


相关文章
对该文的评论


网站简介 - 广告服务 - 网站地图 - 帮助信息 - 联系方式 - English
北京百联美达美数码科技有限公司 版权所有 京ICP证020026号
Copyright © CSDN.NET, Inc. All Rights Reserved
<script> document.write(" "); </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值