XMLCompare可对2个XML进行比较,比较是忽略兄弟节点间的顺序的,只对包含关系和属性进行比较。
写着个程序的目的本来是为了测试我写的一个XML Parser解析结果是否正确。不过这个比较器也可以用于比较其他XML,利用STL中的map和set来存储属性和父子关系,可以轻松实现2个XML在层次逻辑上的比较(不考虑兄弟之间的顺序)。
#include <iostream>
#include <tchar.h>
#include <map>
#include <set>
#include <string>
#include <stack>
#import <msxml4.dll>
using namespace std;
stack <wstring> errpath;
inline void TESTHR( HRESULT _hr )
{ if FAILED(_hr) throw(_hr); }
struct Node
{
map<wstring, wstring> m_mattributes;
multiset<Node> m_childNodes;
wstring text;
wstring name;
bool operator < (const Node& node) const
{
if(name < node.name)
return true;
if(m_mattributes < node.m_mattributes)
return true;
if(text < node.text)
return true;
if(m_childNodes < node.m_childNodes)
return true;
return false;
}
bool operator == (const Node& node) const
{
if(name == node.name && m_mattributes == node.m_mattributes &&
text == node.text && m_childNodes == node.m_childNodes)
return true;
else
{
wstring s = name;
if(m_mattributes.size() > 0)
{
s.append(L"[");
for(map<wstring, wstring>::const_iterator pos=m_mattributes.begin();
pos!=m_mattributes.end(); ++pos){
s.append(L"@");
s.append(pos->first);
s.append(L"='");
s.append(pos->second);
s.append(L"',");
}
s.erase(s.size()-1, 1);
s.append(L"]");
}
errpath.push(s);
return false;
}
}
};
void DOMToNode(MSXML2::IXMLDOMNodePtr nodeptr, Node& parent)
{
Node node;
if(nodeptr->nodeType == NODE_TEXT){
node.text = nodeptr->text;
parent.m_childNodes.insert(node);
return;
}
node.name = nodeptr->nodeName;
map <wstring, wstring> &pm = node.m_mattributes;
MSXML2::IXMLDOMNamedNodeMapPtr mapPtr = nodeptr->attributes;
if (mapPtr == NULL) return;
unsigned int len = mapPtr->length;
MSXML2::IXMLDOMNodePtr tmp;
for(unsigned int i = 0; i<len; ++i) {
tmp = mapPtr->item[i];
pm[wstring(tmp->nodeName)] =
wstring((_bstr_t)tmp->nodeValue );
}
MSXML2::IXMLDOMNodeListPtr pXMLDomNodeList = NULL;
pXMLDomNodeList = nodeptr->childNodes;
if(pXMLDomNodeList != NULL && pXMLDomNodeList->length > 0) {
while(!FAILED(pXMLDomNodeList->raw_nextNode(&tmp)) && (tmp != NULL)){
DOMToNode(tmp, node);
}
}
parent.m_childNodes.insert(node);
}
bool compare_xml(TCHAR* fn1, TCHAR* fn2)
{
MSXML2::IXMLDOMDocumentPtr xdoc1ptr, xdoc2ptr;
try{
TESTHR(CoInitialize(NULL));
TESTHR(xdoc1ptr.CreateInstance(__uuidof(MSXML2::DOMDocument40)));
TESTHR(xdoc2ptr.CreateInstance(__uuidof(MSXML2::DOMDocument40)));
xdoc1ptr->load(_variant_t(fn1));
xdoc2ptr->load(_variant_t(fn2));
Node nd1,nd2;
DOMToNode(xdoc1ptr->documentElement, nd1);
DOMToNode(xdoc2ptr->documentElement, nd2);
if( nd1 == nd2 )
return true;
else
return false;
}
catch(TCHAR* errmsg){
cerr << errmsg << endl;
xdoc1ptr = xdoc2ptr = NULL;
CoUninitialize();
}
catch(...){
cerr << "unknown error occur" << endl;
xdoc1ptr = xdoc2ptr = NULL;
CoUninitialize();
}
return false;
}
int _tmain(int argc, _TCHAR* argv[])
{
if(argc < 3)
{
cout << "too few parameter" << endl;
return 0;
}
locale loc("chs");
wcout.imbue(loc);
if(compare_xml(argv[1], argv[2]))
cout << "compare OK" << endl;
else
{
cout << "compare failed" << endl;
wcout << "on node: ";
while(errpath.size() > 0){
wcout << "/" << errpath.top();
errpath.pop();
}
wcout << endl;
}
CoUninitialize();
return 0;
}