使用 XPath 在 QDomDocument 中搜索节点

Search for nodes in a QDomDocument using XPath

我被 C# 和它在 System.Xml 命名空间中的 XML 操作 classes 宠坏了(当然是烂透了)。我可以将 XML 文件加载到 XmlDocument 中。我可以使用 XmlNode.SelectNodes( "an xpath expression" ) 在整个文档中搜索与 XPath 表达式匹配的节点。结果是 XmlNodeList,其中包含我可以迭代的 XmlNode 个对象。

现在我正在使用C++ Qt(版本4.7.1和4.8,但具体版本可能并不重要)。我可以将 XML 文件加载到 QDomDocument 中。但是,我很沮丧,因为我无法像在 C# 中那样使用 XPath 表达式搜索文档。

我使用 QXmlQuery 在 XML 文件中查找内容的成功率有限。如果我以正确的方式编写查询,我可以获得 QStringList 个结果,迭代 QStringList,然后将数据存储在某个地方以备后用。

但是,我仍然希望能够直接通过 XPath 表达式获取文档中 QDomNode 对象的集合。一个特定的用例是找到一个元素,其 "name" 属性具有特定值,然后用新元素替换该元素。这就是为什么我想要 QDomNode 对象本身,而不仅仅是 QXmlQuery 可以提供的 XML 内容的一些基于字符串或其他表示形式。对于刚刚提到的特定用例,我使用 QDomElement.elementsByTagName() 并迭代这些元素,但它不像 XPath 那样灵活也不像 XPath 那样酷。

难道只是一厢情愿吗?开发一些实现 QAbstractXmlReceiver 接口的新 class 是否值得?或者,我最终会得到一个与 QDomDocument 中的 QDomNode 对象没有直接关系的新数据集合吗?

以下是我使用 XPath 表达式在 QDomDocument 中搜索节点的实用函数。它使用@Alejandro 建议的 QDomNodeModel class,可从 https://adared.ch/qdomnodemodel-qxmlquery. It is based on the usage example from https://www.qtcentre.org/threads/37645-QAbstractXmlNodeModel-implementation-QDomNodeModel-QXmlQuery 下载。感谢 Stanislaw Adaszewski,他提供了 QDomNodeModel class 和用法示例。

QDomNodeModel 中有一些方法被评论为未实现。但是,对于我需要搜索的简单 XML 内容,QDomNodeModel 就足够了 as-is。

//
/// @brief Search for nodes in a QDomDocument using an XPath.
/// @note I cannot return a QDomNodeList, because it has no public methods for adding items to it.
/// @param[in] doc The document to search.
/// @param[in] fromNode The node in the document to start searching from.
///   e.g., to search the whole document, use <code>doc.documentElement()</code>.
/// @param[in] xpath The XPath expression.
/// @return A list of found nodes.
//
QList<QDomNode> findNodes( QDomDocument const & doc, QDomNode const & fromNode, QString const & xpath )
{
  qDebug( "%s", __FUNCTION__ );
  QList<QDomNode> foundNodes;

  //------------------------------
  // The name pool that everybody shares.
  QXmlNamePool pool;

  //------------------------------
  // The model that wraps the document.
  QDomNodeModel model( pool, doc );

  //------------------------------
  // The query.
  // XQuery10 means the default XQuery 1.0 language, as opposed to XSLT20.
  QXmlQuery query( /*QXmlQuery::XQuery10,*/ pool );

  // Operate on the given node.
  QXmlNodeModelIndex fromIndex = model.fromDomNode( fromNode );
  query.setFocus( QXmlItem( fromIndex ) );

  // The query statement.
  query.setQuery( xpath );
  if ( !query.isValid() )
  {
    qDebug( "Query is not valid" );
    return foundNodes;
  }

  //------------------------------
  // The destination for the result of the query.
  QXmlResultItems result;

  //------------------------------
  // Evaluate the query.
  query.evaluateTo( &result );
  if ( result.hasError() )
  {
    qDebug( "Query evaluation failed" );
    return foundNodes;
  }

  //------------------------------
  // The result of the query.
  qDebug( "Query result:" );
  while ( !result.next().isNull() )
  {
    QXmlNodeModelIndex index = result.current().toNodeModelIndex();
    QDomNode node = model.toDomNode( index );
    qDebug( "  %d %s: %s", node.nodeType(), qPrintable( node.nodeName() ), qPrintable( node.nodeValue() ) );
    foundNodes << node;
  }

  return foundNodes;
}

在我的应用程序中,我加载了一个 XML 文件,并使用上面的实用函数来搜索它。

//------------------------------
// The path of the XML file.
QString path = "settings.xml";

//------------------------------
// Open the file.
QFile file( path );
if ( !file.open( QIODevice::ReadOnly ) )
{
  qDebug( "Failed to open '%s': %s", qPrintable( path ), qPrintable( file.errorString() ) );
  return;
}

//------------------------------
// Load the file into a document.
QDomDocument doc;
QString error;
int line;
int column;
if ( !doc.setContent( &file, &error, &line, &column ) )
{
  qDebug( "%s(%d,%d): %s", qPrintable( path ), line, column, qPrintable( error ) );
  return;
}

//------------------------------
// The document root element.
QDomElement rootElem = doc.documentElement();

//------------------------------
// Search for an element whose name attribute has a certain value.
QString name = "Alice";
QString xpath = QString( "setting[@name='%1']" ).arg( name );
QList<QDomNode> foundNodes = findNodes( doc, rootElem, xpath );

//------------------------------
// Did I find it?
if ( foundNodes.size() > 0 )
{
  QDomElement foundElem = foundNodes.at( 0 ).toElement();

  // Do something with that element.      
  ...
} 

要搜索的示例XML内容。

<?xml version='1.0'?>
<settings>
  <setting name="Bob">12</setting>
  <setting name="Carol">34</setting>
  <setting name="Ted">56</setting>
  <setting name="Alice">78</setting>
</settings>