CMarkup是处理XML的一个不错的选择。但是我们下到的免费版本是不支持XPath的,这意味着我们为了查找一个内容,需要多次调用FindElem或再FindChildElem。而不能这样 xml.FindElem("/ROOT/B/C/D")。
而它的Developer版本是支持Path的,但它的Path与XPath仍然不同。详情请参考http://www.firstobject.com/dn_markpath.htm(Paths In CMarkup)英文不好,没仔细看。
但是在免费版本中为了实现Path难道非得一次又一次地调用FindElem或者FindChildElem吗?
通过不太仔细地研读它的源码,我有了以下发现。
FindElem调用了x_FindElem,它的声明如下:
int x_FindElem( int iPosParent, int iPos, PathPos& path ) const;
其中的入参PathPos& path就是记录要查找的路径的,
如果找到了path指定的内容就返回一个数,也就是位置,否则返回0.
在x_FindElem里,有以下代码:
// Paths other than simple tag name are only supported in the developer version
if ( path.IsAnywherePath() || path.IsAbsolutePath() )
return 0;
这意味着我们查找一个路径的时候它根本就不查找了,直接返回了0.
那么我们是不是可以做些工作使它能够返回我们想要的位置呢?
接着看下去,
TokenPos token( m_strDoc, m_nDocFlags );
while ( iPos )
{
// Compare tag name
token.m_nNext = ELEM(iPos).nStart + 1;
token.FindName(); // Locate tag name
if ( token.Match(path.GetPtr()) )
return iPos;
iPos = ELEM(iPos).iElemNext;
}
return 0;
看来这里就是查找的过程了。
看来TokenPos 也是一个比较重要的东西。通过一番调试发现,原来TokenPos 是用来记录当前查找的位置的。在查找时它和要查找的path进行匹配,如果能匹配就返回该位置。
如果path是"/ROOT/B/C/D", token.Match(path.GetPtr()) 为真,它就返回了,在此时我们显然是不希望它返回,而是希望它去查找它的子结点,一直匹配到path的末尾的时候才返回。
所以需要把它改成如下代码
TokenPos token( m_strDoc, m_nDocFlags );
while ( iPos )
{
// Compare tag name
token.m_nNext = ELEM(iPos).nStart + 1;
token.FindName(); // Locate tag name
if ( token.Match(path.GetPtr()) )
{
path.GetWordAndInc();
if (path.IsAtPathEnd())
{
return iPos;
}else
{
path.IncChar();
iPos = ELEM(iPos).iElemChild;
continue;
}
}
iPos = ELEM(iPos).iElemNext;
}
通过以上改动再执行xml.FindElem("/ROOT/B/C/D")就可以返回真了。当然如果想多次调用它还得在FindElem里面改动一下:
bool CMarkup::FindElem( MCD_CSTR szName )
{
if ( m_nDocFlags & MDF_WRITEFILE )
return false;
if ( m_pElemPosTree->GetSize() )
{
// Change current position only if found
PathPos path( szName, false );
//
if(path.IsAbsolutePath())
{
ResetPos();
}
//
int iPos = x_FindElem( m_iPosParent, m_iPos, path );
if ( iPos )
{
// Assign new position
x_SetPos( ELEM(iPos).iElemParent, iPos, 0 );
return true;
}
}
return false;
}
改后的x_FindElem如下
int CMarkup::x_FindElem( int iPosParent, int iPos, PathPos& path ) const
{
// If pPath is NULL or empty, go to next sibling element
// Otherwise go to next sibling element with matching path
//
if ( ! path.ValidPath() )
return 0;
// Paths other than simple tag name are only supported in the developer version
// if ( path.IsAnywherePath() || path.IsAbsolutePath() )
// return 0;
if ( iPos )
iPos = ELEM(iPos).iElemNext;
else
iPos = ELEM(iPosParent).iElemChild;
// Finished here if pPath not specified
if ( ! path.IsPath() )
return iPos;
// Search
TokenPos token( m_strDoc, m_nDocFlags );
while ( iPos )
{
// Compare tag name
token.m_nNext = ELEM(iPos).nStart + 1;
token.FindName(); // Locate tag name
if ( token.Match(path.GetPtr()) )
{
path.GetWordAndInc();
if (path.IsAtPathEnd())
{
return iPos;
}else
{
path.IncChar();
iPos = ELEM(iPos).iElemChild;
continue;
}
}
iPos = ELEM(iPos).iElemNext;
}
return 0;
}
当然以上的改动似乎并没有达到Developer版本的水平,还希望高手支招啊!