最精简的 XML 解析器






3.18/5 (22投票s)
2004年3月22日
2分钟阅读

118244

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
替换,这让我省略了 simplearray
和 equalhelper
。 并且添加了一个新的构造函数,因为我的个人使用,你可以直接删除它。 在上一个版本中,我仅当 innertext
的长度大于 0
时才添加它,这不太合理,所以我修改了它。
性能仍然需要大量工作。 如果你不需要在整个运行时更改节点项,你可以将 CComBSTR
变为 LPCTSTR
,这将消耗更少的内存,并且性能会相应地提高。
最后一句话,也是最重要的一句话,显然你应该注意的是,如果你在你的商业产品中使用我的代码,你当然应该给我一部分利润! 我是认真的,先生! 否则我会报警!
无论如何,非常感谢您的评论,这样我才能让它对我们所有人更好。 我在这里等着你。
许可证
本文没有附带明确的许可证,但可能在文章正文或下载文件本身中包含使用条款。 如果有疑问,请通过下面的讨论板联系作者。 可以在 此处 找到作者可能使用的许可证列表。