使用pugixml库在qt环境下操作xml,支持事务

#ifndef XMLTOOL_H
#define XMLTOOL_H

#include <QString>
#include <QMap>
#include <QMutex>
#include <QWaitCondition>
#include <pugixml.hpp>
#include <QFile>
#include <QTextStream>

class XmlTool {
public:
    // 事务操作
    static bool beginTransaction(const QString& fileName);
    static bool commitTransaction(const QString& fileName);
    static bool rollbackTransaction(const QString& fileName);

    // XML 文件操作
    static bool loadXmlStream(const QString& fileName, pugi::xml_document& doc);
    static bool saveXmlStream(const QString& fileName, const pugi::xml_document& doc);

    // 节点操作
    static bool addNode(const QString& fileName, const QString& parentXPath, const QString& nodeName, const QMap<QString, QString>& attributes);
    static bool removeNode(const QString& fileName, const QString& xpath);
    static bool updateNode(const QString& fileName, const QString& xpath, const QMap<QString, QString>& attributes);

    // 查询和排序
    static QList<pugi::xml_node> query(const QString& fileName, const QString& xpath);
    static QList<pugi::xml_node> queryAndSort(const QString& fileName, const QString& xpath, const QList<QPair<QString, bool>>& sortRules);

    // 用于格式化节点的缩进
    static void add_indentation(pugi::xml_node node, int indent_level, std::ostringstream &oss);

    // 处理 <if> 标签并更新树结构
    static void process_if_node(pugi::xml_node if_node, pugi::xml_node &parent_node);

    // 处理 <for> 标签并展开它们
    static void process_for_node(pugi::xml_node for_node, pugi::xml_node &parent_node);

    // 递归处理所有嵌套的 <if> 节点
    static void process_all_if_nodes(pugi::xml_node node);

    // 递归处理所有嵌套的 <for> 节点
    static void process_all_for_nodes(pugi::xml_node node);

    // 处理 XML 文件并输出结果
    static int docFrameContent(std::string input_path, std::string output_path);

private:
    //加载文件
    static pugi::xml_document* loadXml(const QString& fileName);

    // 确保文档已加载
    static void ensureDocumentLoaded(const QString& fileName);
    static bool processXmlChunk(const QString& chunk, pugi::xml_document& doc);

    static QString xmlDocumentToString(const pugi::xml_document& doc);

    // 锁和条件变量
    static void ensureLock(const QString& fileName);
    static void waitForUnlock(const QString& fileName);
    static void releaseLock(const QString& fileName);

    //判断节点是否存在属性
    static bool hasAttribute(const pugi::xml_node& node, const QString &attributeName);
    //设置节点属性值
    static void setNodeAttribute(pugi::xml_node& newNode,QString attrName,QString attrValue);

    // 节点和属性排序
    static QList<pugi::xml_node> sortNodesByRules(const QList<pugi::xml_node>& nodes, const QList<QPair<QString, bool>>& sortRules);
    static void sortNodesByTagName(pugi::xml_node& parent);
    static void sortAttributesByAttributeName(pugi::xml_node& node);
    //整体xml文档进行排序
    static void sortNodeRecursively(pugi::xml_node& node);

    // 成员变量
    static QMap<QString, pugi::xml_document*> xmlDocs_;         // 存储加载的XML文档
    static QMap<QString, QMutex*> fileLocks_;                  // 文件锁
    static QMap<QString, QWaitCondition*> fileConditions_;     // 文件条件变量
    static QMutex globalMutex_;                                // 全局锁,用于保护访问共享资源
    static QMap<QString, bool> transactionInProgress_;         // 事务状态
    static QMap<QString, QString> transactionBackups_;         // 事务备份(保存文档的字符串形式)
};
#endif // XMLTOOL_H
#include "XmlTool.h"
#include <QDebug>
#include <sstream>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>

QMap<QString, pugi::xml_document*> XmlTool::xmlDocs_;
QMap<QString, QMutex*> XmlTool::fileLocks_;
QMap<QString, QWaitCondition*> XmlTool::fileConditions_;
QMutex XmlTool::globalMutex_;
QMap<QString, bool> XmlTool::transactionInProgress_;
QMap<QString, QString> XmlTool::transactionBackups_;

bool XmlTool::beginTransaction(const QString& fileName) {
    QMutexLocker globalLocker(&globalMutex_);
    ensureLock(fileName);
    if (!transactionInProgress_.contains(fileName)) {
        transactionBackups_[fileName] = xmlDocs_.contains(fileName) ? xmlDocumentToString(*xmlDocs_[fileName]) : "";
        transactionInProgress_[fileName] = true;
        return true;
    }
    return false;
}

bool XmlTool::commitTransaction(const QString& fileName) {
    QMutexLocker globalLocker(&globalMutex_);
    ensureLock(fileName);
    if (transactionInProgress_.contains(fileName)) {
        transactionInProgress_.remove(fileName);
        transactionBackups_.remove(fileName);
        return true;
    }
    return false;
}

bool XmlTool::rollbackTransaction(const QString& fileName) {
    QMutexLocker globalLocker(&globalMutex_);
    ensureLock(fileName);
    if (transactionInProgress_.contains(fileName)) {
        if (transactionBackups_.contains(fileName)) {
            QString backupData = transactionBackups_[fileName];
            delete xmlDocs_[fileName];
            xmlDocs_[fileName] = new pugi::xml_document();
            std::istringstream iss(backupData.toStdString());
            xmlDocs_[fileName]->load(iss);
        }
        transactionInProgress_.remove(fileName);
        transactionBackups_.remove(fileName);
        return saveXmlStream(fileName, *xmlDocs_[fileName]);
    }
    return false;
}

bool XmlTool::loadXmlStream(const QString& fileName, pugi::xml_document& doc) {
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "Failed to open XML file for reading:" << fileName;
        return false;
    }
    QByteArray xmlData = file.readAll();
    file.close();
    // Convert QByteArray to std::string
    std::string xmlString = xmlData.toStdString();
    qWarning()<<"xml content:"<<xmlString.c_str();
    // Use load_buffer instead of load
    pugi::xml_parse_result result = doc.load_buffer(xmlString.c_str(), xmlString.size(), pugi::parse_default);
    if (!result) {
        qWarning() << "Failed to parse XML file:" << fileName;
        return false;
    }
    return true;
}


bool XmlTool::saveXmlStream(const QString& fileName, const pugi::xml_document& doc) {
    pugi::xml_node root = doc.child("root");
    // 创建一个格式化的字符串流
    std::ostringstream oss;
    oss << "<?xml version=\"1.0\"?>";
    // 添加根节点的格式化
    add_indentation(root, 0, oss);
    // 保存格式化后的 XML 文件
    std::ofstream output(fileName.toStdString().c_str());
    if (!output) {
        std::cerr << "Failed to open the file " << fileName.toStdString().c_str() << std::endl;
        return 1;
    }
    output << oss.str();
    std::cout << "XML updated and formatted successfully." << std::endl;
    return true;
}

void XmlTool::setNodeAttribute(pugi::xml_node& newNode,QString attrName,QString attrValue)
{
    if(hasAttribute(newNode,attrName)){
        newNode.attribute(attrName.toStdString().c_str()) = attrValue.toStdString().c_str();
    }else{
        newNode.append_attribute(attrName.toStdString().c_str()) = attrValue.toStdString().c_str();
    }
}

bool XmlTool::addNode(const QString& fileName, const QString& parentXPath, const QString& nodeName, const QMap<QString, QString>& attributes) {
    // 调整后的代码:将 beginTransaction 外置
    if (!beginTransaction(fileName)) {
        return false;
    }
    pugi::xml_document* doc = loadXml(fileName);
    try {
        pugi::xml_node parent = doc->select_node(parentXPath.toStdString().c_str()).node();
        if (parent) {
            pugi::xml_node newNode = parent.append_child(nodeName.toStdString().c_str());
            if (!newNode) {
                qWarning() << "Failed to append child node:" << nodeName;
                rollbackTransaction(fileName);
                return false;
            }
            for (auto attrIt = attributes.begin(); attrIt != attributes.end(); ++attrIt) {
                setNodeAttribute(newNode,attrIt.key(),attrIt.value());
            }
            sortNodesByTagName(parent);
            sortAttributesByAttributeName(newNode);
            if (commitTransaction(fileName)) {
                return saveXmlStream(fileName, *doc);
            } else {
                rollbackTransaction(fileName);
                qWarning() << "Failed to commit transaction after adding node.";
                return false;
            }
        } else {
            qWarning() << "Parent node not found for XPath:" << parentXPath;
        }

        rollbackTransaction(fileName);
    } catch (...) {
        rollbackTransaction(fileName);
    }
    return false;
}

bool XmlTool::removeNode(const QString& fileName, const QString& xpath) {
    // 调整后的代码:将 beginTransaction 外置
    if (!beginTransaction(fileName)) {
        return false;
    }
    pugi::xml_document* doc = loadXml(fileName);
    try {
        QList<pugi::xml_node> nodes = query(fileName,xpath);
        if(nodes.size()>0){
            for(pugi::xml_node node:nodes){
                pugi::xml_node parent = node.parent();
                parent.remove_child(node);
            }
            if (commitTransaction(fileName)) {
                return saveXmlStream(fileName, *doc);
            } else {
                rollbackTransaction(fileName);
                qWarning() << "Failed to commit transaction after removing node.";
                return false;
            }
        } else {
            qWarning() << "Node not found for XPath:" << xpath;
        }

        rollbackTransaction(fileName);
    } catch (...) {
        rollbackTransaction(fileName);
    }
    return false;
}

bool XmlTool::updateNode(const QString& fileName, const QString& xpath, const QMap<QString, QString>& attributes) {
    // 调整后的代码:将 beginTransaction 外置
    if (!beginTransaction(fileName)) {
        return false;
    }
    pugi::xml_document* doc = loadXml(fileName);
    try {
        pugi::xml_node node = doc->select_node(xpath.toStdString().c_str()).node();
        if (node) {
            // 清除旧的属性
            pugi::xml_attribute attr = node.first_attribute();
            while (attr) {
                pugi::xml_attribute nextAttr = attr.next_attribute(); // 先保存下一个属性
                node.remove_attribute(attr.name()); // 移除当前属性
                attr = nextAttr; // 更新当前属性
            }

            // 添加新的属性
            for (auto attrIt = attributes.begin(); attrIt != attributes.end(); ++attrIt) {
                setNodeAttribute(node,attrIt.key(),attrIt.value());
            }

            sortAttributesByAttributeName(node);

            if (commitTransaction(fileName)) {
                return saveXmlStream(fileName, *doc);
            } else {
                rollbackTransaction(fileName);
                qWarning() << "Failed to commit transaction after updating node.";
                return false;
            }

        } else {
            qWarning() << "Node not found for XPath:" << xpath;
        }
        rollbackTransaction(fileName);
    } catch (...) {
        rollbackTransaction(fileName);
    }
    return false;
}

QList<pugi::xml_node> XmlTool::query(const QString& fileName, const QString& xpath) {
    QList<pugi::xml_node> result;
    QMutexLocker globalLocker(&globalMutex_);
    ensureLock(fileName);
    pugi::xml_document* doc = loadXml(fileName);
    pugi::xpath_node_set nodes = doc->select_nodes(xpath.toStdString().c_str());
    for (pugi::xpath_node_set::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
        result.append(it->node());
    }
    return result;
}

QList<pugi::xml_node> XmlTool::queryAndSort(const QString& fileName, const QString& xpath, const QList<QPair<QString, bool>>& sortRules) {
    QList<pugi::xml_node> result = query(fileName, xpath);
    return sortNodesByRules(result, sortRules);
}

pugi::xml_document* XmlTool::loadXml(const QString &fileName)
{
    pugi::xml_document* doc = new pugi::xml_document();
    if (!loadXmlStream(fileName, *doc)) {
        delete doc;
        xmlDocs_.remove(fileName);
    } else {
        xmlDocs_[fileName] = doc;
    }
    return  doc;
}

void XmlTool::ensureDocumentLoaded(const QString& fileName) {
    if (!xmlDocs_.contains(fileName)) {
        loadXml(fileName);
    }
}

bool XmlTool::processXmlChunk(const QString& chunk, pugi::xml_document& doc) {
    pugi::xml_parse_result result = doc.load_buffer(chunk.toStdString().c_str(), chunk.size());
    return result;
}

void XmlTool::ensureLock(const QString& fileName) {
    if (!fileLocks_.contains(fileName)) {
        fileLocks_[fileName] = new QMutex();
    }
    if (!fileConditions_.contains(fileName)) {
        fileConditions_[fileName] = new QWaitCondition();
    }
}

void XmlTool::waitForUnlock(const QString& fileName) {
    fileLocks_[fileName]->lock();
    fileConditions_[fileName]->wait(fileLocks_[fileName]);
    fileLocks_[fileName]->unlock();
}

void XmlTool::releaseLock(const QString& fileName) {
    fileLocks_[fileName]->lock();
    fileConditions_[fileName]->wakeAll();
    fileLocks_[fileName]->unlock();
}

QList<pugi::xml_node> XmlTool::sortNodesByRules(const QList<pugi::xml_node>& nodes, const QList<QPair<QString, bool>>& sortRules) {
    QList<pugi::xml_node> sortedNodes = nodes;
    std::sort(sortedNodes.begin(), sortedNodes.end(), [&](const pugi::xml_node& a, const pugi::xml_node& b) {
        for (const auto& rule : sortRules) {
            bool asc = rule.second;
            QString attrName = rule.first.toStdString().c_str();
            std::string attrA = a.attribute(attrName.toStdString().c_str()).as_string();
            std::string attrB = b.attribute(attrName.toStdString().c_str()).as_string();
            if (attrA != attrB) {
                return (attrA < attrB) == asc;
            }
        }
        return false;
    });
    return sortedNodes;
}

void XmlTool::sortNodesByTagName(pugi::xml_node& parent) {
    std::vector<pugi::xml_node> nodes;
    for (pugi::xml_node child : parent.children()) {
        nodes.push_back(child);
    }
    std::sort(nodes.begin(), nodes.end(), [](const pugi::xml_node& a, const pugi::xml_node& b) {
        return std::string(a.name()) < std::string(b.name());
    });

    // 首先,移除所有子节点
    for (pugi::xml_node node : parent.children()) {
        parent.remove_child(node);
    }

    // 然后,按排序后的顺序重新添加它们
    for (const auto& node : nodes) {
        parent.append_copy(node);
    }
}


void XmlTool::sortAttributesByAttributeName(pugi::xml_node& node) {
    std::vector<pugi::xml_attribute> attrs;
    for (pugi::xml_attribute attr : node.attributes()) {
        attrs.push_back(attr);
    }
    std::sort(attrs.begin(), attrs.end(), [](const pugi::xml_attribute& a, const pugi::xml_attribute& b) {
        return std::string(a.name()) < std::string(b.name());
    });
    for (const auto& attr : attrs) {
        node.append_attribute(attr.name()) = attr.value();
    }
}

QString XmlTool::xmlDocumentToString(const pugi::xml_document& doc) {
    std::ostringstream oss;
    doc.save(oss, "", pugi::format_default, pugi::encoding_utf8);
    return QString::fromStdString(oss.str());
}

void XmlTool::sortNodeRecursively(pugi::xml_node& node) {
    // 对当前节点的属性进行排序
    sortAttributesByAttributeName(node);
    // 对当前节点的所有子节点进行排序
    sortNodesByTagName(node);
    // 递归排序子节点的子节点
    for (pugi::xml_node child : node.children()) {
        sortNodeRecursively(child);
    }
}


// 检查xml节点中是否存在指定属性
bool XmlTool::hasAttribute(const pugi::xml_node& node, const QString &attributeName) {
    return node.attribute(attributeName.toStdString().c_str()) != nullptr;
}


// 用于格式化节点的缩进
void XmlTool::add_indentation(pugi::xml_node node, int indent_level, std::ostringstream &oss) {
    std::string indent(indent_level * 2, ' ');
    if (node.type() == pugi::node_element) {
        oss << "\n" << indent << "<" << node.name();
        for (pugi::xml_attribute attr = node.first_attribute(); attr; attr = attr.next_attribute()) {
            oss << " " << attr.name() << "=\"" << attr.value() << "\"";
        }
        oss << ">";

        bool has_children = false;
        for (pugi::xml_node child = node.first_child(); child; child = child.next_sibling()) {
            if (child.type() != pugi::node_pcdata) {
                has_children = true;
                break;
            }
        }

        // 处理子节点
        if (has_children) {
            for (pugi::xml_node child = node.first_child(); child; child = child.next_sibling()) {
                add_indentation(child, indent_level + 1, oss);
            }
            oss << "\n" << indent << "</" << node.name() << ">";
        } else {
            std::string text = node.child_value();
            if (text.find_first_not_of(" \t\n\r") != std::string::npos) {
                oss << text;
            }
            oss << "</" << node.name() << ">";
        }
    } else if (node.type() == pugi::node_pcdata) {
        std::string text = node.value();
        text.erase(0, text.find_first_not_of(" \t\n\r"));
        text.erase(text.find_last_not_of(" \t\n\r") + 1);
        if (!text.empty()) {
            oss << text;
        }
    }
}

// 处理 <if> 标签并更新树结构
void XmlTool::process_if_node(pugi::xml_node if_node, pugi::xml_node &parent_node) {
    std::string condition = if_node.attribute("condition").as_string();
    bool condition_met = (condition == "true");

    if (condition_met) {
        // 如果条件满足,将子节点直接插入到父节点中
        for (pugi::xml_node child = if_node.first_child(); child; child = child.next_sibling()) {
            parent_node.insert_copy_before(child, if_node);
        }
    }
    // 无论条件是否满足,都移除 <if> 节点
    parent_node.remove_child(if_node);
}


// 处理 <for> 标签并展开它们
void XmlTool::process_for_node(pugi::xml_node for_node, pugi::xml_node &parent_node) {
    int count = for_node.attribute("count").as_int();
    if (count <= 0) {
        std::cerr << "Invalid 'count' attribute" << std::endl;
        return;
    }

    std::vector<pugi::xml_node> children_to_copy;
    for (pugi::xml_node child : for_node.children()) {
        children_to_copy.push_back(child);
    }

    for (int i = 0; i < count; ++i) {
        for (pugi::xml_node child : children_to_copy) {
            pugi::xml_node new_node = parent_node.insert_copy_before(child, for_node);

            // 递归处理嵌套的 <for> 和 <if> 节点
            process_all_if_nodes(new_node);
            process_all_for_nodes(new_node);
        }
    }

    // 最后移除 <for> 节点
    parent_node.remove_child(for_node);
}

// 递归处理所有嵌套的 <if> 节点
void XmlTool::process_all_if_nodes(pugi::xml_node node) {
    pugi::xml_node next_node;
    for (pugi::xml_node child = node.first_child(); child; child = next_node) {
        next_node = child.next_sibling();
        if (std::string(child.name()) == "if") {
            process_if_node(child, node);
        } else {
            process_all_if_nodes(child); // 递归调用
        }
    }
}

// 递归处理所有嵌套的 <for> 节点
void XmlTool::process_all_for_nodes(pugi::xml_node node) {
    pugi::xml_node next_node;
    for (pugi::xml_node child = node.first_child(); child; child = next_node) {
        next_node = child.next_sibling();
        if (std::string(child.name()) == "for") {
            process_for_node(child, node);
        } else {
            process_all_for_nodes(child); // 递归调用
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追Star仙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值