65.9K
CodeProject 正在变化。 阅读更多。
Home

最精简的 XML 解析器

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.18/5 (22投票s)

2004年3月22日

2分钟阅读

viewsIcon

118244

downloadIcon

1348

一个替代庞大 MSXML 的 XML 解析器。

引言

XML 是构建对象模型时必不可少的数据结构。 没有它,你几乎无法处理任何分层结构。 MSXML 是一个庞大的 DLL,并且其版本不断变化,使得安装变得不太容易。 另一方面,我只需要解析一个小文档,那只是一个小应用程序,但我需要分发一个大的 DLL。 这些促使我想重新造轮子。

在我完成草稿后,我发现已经有一些关于 XML 解析的成功项目,例如 TinyXml,基于简单 STL 的 XML 解析器,作者 David Hubbard,XMLite: 简单 XML 解析器,作者 Cho, Kyung-min 等。 它们各自具有自己的特点,而我的需求与上述不同,我的代码片段不涉及 MFC、STL,并且不需要在其他操作系统上运行。

Using the Code

#pragma once
#define MAX_TEXT_LEN 0x400
#define MAX_NAME_LEN 0x100

class CAxNode
{
public:
  CAxNode() { parent = NULL; }
  CAxNode(CAxNode* p):parent(p){}
  ~CAxNode()
  {
    for(long i=0; i<childNodes.GetSize();i++)
      delete childNodes[i];
  }

  CComBSTR  elementType;
  CSimpleArray<CComBSTR> arrText;
  CSimpleMap<CComBSTR,CComBSTR> attrMap;
  CSimpleArray<CAxNode*> childNodes;
  CAxNode*   parent;
};

inline void FillSTR(LPTSTR& s,CComBSTR &x)
{
  TCHAR name[MAX_NAME_LEN];
  long j=0;
  while(name[j++]=*s++)    
  // should be char or dig, and not dig head
  if(*s==_T('\r')||*s==_T('\n')||
    *s==_T('\t')||*s==_T(' ')||*s==_T('>')||
    *s==_T('=')||*s==_T('<')||*s==_T('/'))
    break;

  name[j] =_T('\0');
  //CharLower(name);
  x = name;
}

inline void SkipFormatChar(LPCTSTR& s)
{
  while(*s++)
    if(*s!=_T('\r')&&*s!=_T('\n')&&*s!=_T('\t')&&*s!=_T(' '))
      break;
}
inline void ParseNode(CAxNode* Node, LPTSTR& s)
{
  while(*s++!=_T('<'));
  FillSTR(s,Node->elementType);

  TCHAR szText[MAX_TEXT_LEN];
  if(*s!=_T('>'))
  {
    // now spawn attr map
    SkipFormatChar(s);
    while(*s)
    {
      CComBSTR attr;
      FillSTR(s,attr);
      while(*s++!=_T('"'));

      long j = 0;
      while(*s!=_T('"')&&j<MAX_TEXT_LEN)
        szText[j++] = *s++;
      szText[j] = _T('\0');

      // remove duplicate attributes
      if(Node->attrMap.FindKey(attr)<0)
        Node->attrMap.Add(attr,szText);

      SkipFormatChar(s);
      if(*s==_T('/'))
      {
        while(*s++!='>');
        return;  // closeness of some self-closed tag
      }

      if(*s==_T('>'))  
        break;  // now process innertext
    }
  }

  // processing child nodes
  while(*s)
  {
    if(*s==_T('>'))  
      *s++;

    long j = 0;
    while(*s!=_T('<')&&j<MAX_TEXT_LEN) 
      szText[j++] = *s++;
    szText[j] = _T('\0');
    Node->arrText.Add(szText);
  
    SkipFormatChar(s);
    if(*s==_T('/')) 
    {
      SkipFormatChar(s);
      CComBSTR elementType;
      FillSTR(s,elementType);
      ATLASSERT(Node->elementType==elementType);
      return;
    }
    *s--;

    CAxNode* child = new CAxNode(Node);
    Node->childNodes.Add(child);
    ParseNode(child,s);
  }
  return;
}

#pragma warning(push)
#pragma warning(disable: 4244) 
inline void RemoveBlock(LPTSTR &s,LPCTSTR szleft,LPCTSTR szright)
{
  long i1,i2;
  LPTSTR s1,s2;
  while(1)
  {
    s1 = _tcsstr(s,szleft);
    if(s1==NULL)
      break;
    i1 = s1-s;
    if(i1<0) 
      break;
    s2 = _tcsstr(s,szright);
    i2 = s2-s+lstrlen(szright);
    if(i2<0) 
      break;

    ATLASSERT(i2>i1);
    for(int i=i1;i<i2;i++)
      s[i] = _T(' ');
  }
  return;
}
#pragma warning(pop)

这就是所有的代码,总共 135 行。 为了使用它,你只需要 atlsimplecoll.h 文件。 如果你没有,你可以从其他地方复制,这个文件对其他的 ATL 头文件依赖很少。

HANDLE hFile = ::CreateFile(testfile,GENERIC_READ,
  FILE_SHARE_WRITE,NULL,OPEN_EXISTING,NULL,NULL);
if (hFile == INVALID_HANDLE_VALUE)
  return -1;

ULARGE_INTEGER liFileSize;
liFileSize.LowPart = ::GetFileSize(hFile, &liFileSize.HighPart);
if (liFileSize.LowPart == 0xFFFFFFFF)
  return -1;

LPTSTR lpXML = 
 new TCHAR[((size_t)liFileSize.QuadPart)/sizeof(TCHAR)+1];

DWORD pdwRead;
BOOL b = ::ReadFile(hFile, lpXML, 
  (DWORD)liFileSize.QuadPart,&pdwRead,NULL);
lpXML[pdwRead/sizeof(TCHAR)]= _T('\0');
  
RemoveBlock(lpXML,_T("<?"),_t("?>"));
RemoveBlock(lpXML,_T("<!--"),_T("-->"));

CAxNode* pNode = new CAxNode(NULL);
  
LPTSTR xxx = lpXML;
DWORD dw1 = GetTickCount();
ParseNode(pNode,xxx);
DWORD dw2 = GetTickCount();  

delete [] lpXML;
  
// remove child at 0,0
//CAxNode* child = pNode->childNodes[0]->childNodes[0];
//delete child;
//pNode->childNodes[0]->childNodes.RemoveAt(0);

delete pNode;
::CloseHandle(hFile);   

历史

如果你阅读了我的上一个版本,你可以看到我做了很多更改,例如 LPTSTR 已被 CComBSTR 替换,这让我省略了 simplearrayequalhelper。 并且添加了一个新的构造函数,因为我的个人使用,你可以直接删除它。 在上一个版本中,我仅当 innertext 的长度大于 0 时才添加它,这不太合理,所以我修改了它。

性能仍然需要大量工作。 如果你不需要在整个运行时更改节点项,你可以将 CComBSTR 变为 LPCTSTR,这将消耗更少的内存,并且性能会相应地提高。

最后一句话,也是最重要的一句话,显然你应该注意的是,如果你在你的商业产品中使用我的代码,你当然应该给我一部分利润! 我是认真的,先生! 否则我会报警!

无论如何,非常感谢您的评论,这样我才能让它对我们所有人更好。 我在这里等着你。

许可证

本文没有附带明确的许可证,但可能在文章正文或下载文件本身中包含使用条款。 如果有疑问,请通过下面的讨论板联系作者。 可以在 此处 找到作者可能使用的许可证列表。

© . All rights reserved.