You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Qt的QDomDocument中通过XPath获取QDomNode集合?

Hey there! Let's break down your problem and find a solution that fits your Qt 4.7/4.8 workflow.

Is your requirement achievable?

Absolutely. Even though Qt 4.x doesn’t have native support for directly fetching QDomNode collections via XPath like C#’s XmlDocument.SelectNodes(), we can work around this with custom code using Qt’s existing XML APIs.

Is implementing QAbstractXmlReceiver worth it?

Yes, it’s a solid approach for your use case. The default behavior of QXmlQuery returns string results, but creating a custom receiver lets you capture meaningful identifiers for matching nodes, which you can then map back to your original QDomDocument to get the actual QDomNode objects you need for editing.

Can you associate results back to the original QDomNode?

You can’t get the exact QDomNode directly from QXmlQuery (since it uses a separate XML data model), but you can correlate results by generating unique identifiers for nodes in your XQuery expression, then using those identifiers to find the matching nodes in your QDomDocument.

A Practical Implementation Example

Here’s how to put this together:

First, create a custom QAbstractXmlReceiver that captures node identifiers and maps them back to your QDomDocument:

#include <QAbstractXmlReceiver>
#include <QDomDocument>
#include <QList>
#include <QXmlQuery>

class DomNodeReceiver : public QAbstractXmlReceiver
{
public:
    DomNodeReceiver(QDomDocument doc, QList<QDomNode>& resultNodes)
        : m_doc(doc), m_resultNodes(resultNodes) {}

    void atomizedValue(const QVariant& value) override
    {
        // Parse the unique identifier we'll generate in our XQuery
        const QString identifier = value.toString();
        const QStringList parts = identifier.split("|");
        if (parts.size() != 2) return;

        const QString nodeCriteria = parts[0];
        const int position = parts[1].toInt();

        const QStringList criteriaParts = nodeCriteria.split("@");
        if (criteriaParts.size() != 2) return;

        const QString tagName = criteriaParts[0];
        const QStringList attrParts = criteriaParts[1].split("=");
        if (attrParts.size() != 2) return;

        const QString attrName = attrParts[0];
        const QString attrValue = attrParts[1];

        // Find the matching node in the original QDomDocument
        QDomNodeList nodes = m_doc.elementsByTagName(tagName);
        int matchCount = 0;
        for (int i = 0; i < nodes.count(); ++i) {
            QDomElement elem = nodes.at(i).toElement();
            if (elem.hasAttribute(attrName) && elem.attribute(attrName) == attrValue) {
                if (++matchCount == position) {
                    m_resultNodes.append(elem);
                    break;
                }
            }
        }
    }

    // We need to override these empty methods since they're pure virtual
    void startElement(const QString&, const QXmlNodeModelIndex&) override {}
    void endElement() override {}
    void startAttribute(const QString&, const QXmlNodeModelIndex&) override {}
    void endAttribute() override {}
    void startNamespaceBinding(const QString&, const QString&) override {}
    void endNamespaceBinding() override {}
    void characters(const QString&) override {}
    void comment(const QString&) override {}
    void processingInstruction(const QString&, const QString&) override {}
    void atomicValue(const QVariant&) override {}
    void endDocument() override {}
    void startDocument() override {}

private:
    QDomDocument m_doc;
    QList<QDomNode>& m_resultNodes;
};

Then, use this receiver with QXmlQuery to get your QDomNode list:

int main() {
    QDomDocument doc;
    // Load your XML into doc (example):
    QFile xmlFile("your_file.xml");
    if (!xmlFile.open(QIODevice::ReadOnly)) {
        // Handle error
        return 1;
    }
    if (!doc.setContent(&xmlFile)) {
        xmlFile.close();
        // Handle error
        return 1;
    }
    xmlFile.close();

    QXmlQuery query;
    query.bindVariable("doc", doc);
    // XQuery to generate unique identifiers for nodes with name='target'
    query.setQuery(R"(
        declare variable $doc external;
        for $node in $doc//*[@name='target']
        return concat(local-name($node), '@name=', $node/@name, '|', position())
    )");

    QList<QDomNode> matchedNodes;
    DomNodeReceiver receiver(doc, matchedNodes);
    if (!query.evaluateTo(&receiver)) {
        // Handle query error
        return 1;
    }

    // Now you can modify the matched QDomNodes directly
    for (QDomNode node : matchedNodes) {
        QDomElement elem = node.toElement();
        // Example: Replace the element's text content
        elem.setText("Updated content for target node");
    }

    // Save the modified document back to file if needed
    QFile outputFile("modified_file.xml");
    if (outputFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
        QTextStream stream(&outputFile);
        doc.save(stream, 4); // 4 spaces for indentation
        outputFile.close();
    }

    return 0;
}

Notes on This Approach

  • This method uses unique identifiers (tag name + attribute value + position) to avoid ambiguity when multiple nodes match your XPath.
  • If you need to match more complex XPath expressions, you can adjust the XQuery to generate more detailed identifiers (e.g., including parent node paths).
  • If you ever upgrade to Qt 5+, you’ll have access to QDomXPath, which lets you query QDomNodeList directly via XPath without this workaround.

内容的提问来源于stack exchange,提问作者Mike Finch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:25:01