From d00178b95fdcfd3d71ca37ec6bb8c048283d9a04 Mon Sep 17 00:00:00 2001 From: "U. Bruhin" Date: Thu, 18 Sep 2025 17:17:12 +0200 Subject: [PATCH] Migrate from QDomDocument to QXmlStreamReader QDomDocument is deprecated in Qt6 and requires a separate Qt module (QtXml). Qt docs recommend to use QXmlStreamReader instead, which is even part of QtCore. --- CMakeLists.txt | 10 ++--- parseagle/board/board.cpp | 11 +----- parseagle/common/domelement.cpp | 64 +++++++++++++++++++++++++------ parseagle/common/domelement.h | 9 ++++- parseagle/library.cpp | 13 ++----- parseagle/schematic/schematic.cpp | 13 ++----- 6 files changed, 70 insertions(+), 50 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7433b20..57f0f47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ project(parseagle # If Qt version is not explicitly specified, automatically determine it if(NOT PARSEAGLE_QT_MAJOR_VERSION) - find_package(Qt NAMES Qt5 Qt6 REQUIRED COMPONENTS Core Xml) + find_package(Qt NAMES Qt5 Qt6 REQUIRED COMPONENTS Core) set(PARSEAGLE_QT_MAJOR_VERSION ${Qt_VERSION_MAJOR}) endif() set(PARSEAGLE_QT "Qt${PARSEAGLE_QT_MAJOR_VERSION}") @@ -26,7 +26,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # Find required Qt packages -find_package(${PARSEAGLE_QT} REQUIRED COMPONENTS Core Xml) +find_package(${PARSEAGLE_QT} REQUIRED COMPONENTS Core) message(STATUS "Building parseagle with Qt ${${PARSEAGLE_QT}_VERSION}") # Export library @@ -119,11 +119,7 @@ add_library(parseagle STATIC parseagle/symbol/pin.h parseagle/symbol/symbol.h ) -target_link_libraries(parseagle - # Qt - ${PARSEAGLE_QT}::Core - ${PARSEAGLE_QT}::Xml -) +target_link_libraries(parseagle ${PARSEAGLE_QT}::Core) target_include_directories(parseagle INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") # Alias to namespaced variant diff --git a/parseagle/board/board.cpp b/parseagle/board/board.cpp index 20c4801..bfb435e 100644 --- a/parseagle/board/board.cpp +++ b/parseagle/board/board.cpp @@ -28,15 +28,8 @@ Board::~Board() noexcept void Board::load(const QByteArray& content, QStringList* errors) { - QDomDocument doc; - doc.implementation().setInvalidDataPolicy(QDomImplementation::ReturnNullNode); - QString errMsg; - if (!doc.setContent(content, &errMsg)) { - throw std::runtime_error( - "Error while parsing EAGLE board: " + errMsg.toStdString()); - } - DomElement root(doc.documentElement()); - DomElement drawing = root.getFirstChild("drawing"); + const DomElement root = DomElement::parse(content); + const DomElement drawing = root.getFirstChild("drawing"); if (drawing.hasChild("grid")) { mGrid = Grid(drawing.getFirstChild("grid")); diff --git a/parseagle/common/domelement.cpp b/parseagle/common/domelement.cpp index e3c3782..7d66e99 100644 --- a/parseagle/common/domelement.cpp +++ b/parseagle/common/domelement.cpp @@ -3,22 +3,27 @@ namespace parseagle { -DomElement::DomElement(const QDomElement& root) +DomElement::DomElement(QXmlStreamReader& reader) { - if (root.isNull()) { - throw std::runtime_error("Invalid XML node!"); + mName = reader.name().toString(); + for (const QXmlStreamAttribute& attr : reader.attributes()) { + mAttributes.insert(attr.name().toString(), attr.value().toString()); } - mName = root.tagName(); - mText = root.text(); - - QDomNamedNodeMap map = root.attributes(); - for (int i = 0; i < map.count(); i++) { - mAttributes.insert(map.item(i).nodeName(), map.item(i).nodeValue()); + while (!reader.atEnd()) { + reader.readNext(); + if (reader.isCharacters()) { + mText.append(reader.text().toString()); + } else if (reader.isStartElement()) { + mChilds.append(DomElement(reader)); + } else if (reader.isEndElement()) { + break; + } } - for (QDomElement c = root.firstChildElement(); !c.isNull(); c = c.nextSiblingElement()) { - mChilds.append(DomElement(c)); + if (reader.hasError()) { + const QString err = "Failed to parse XML: " + reader.errorString(); + throw std::runtime_error(err.toStdString()); } } @@ -26,6 +31,40 @@ DomElement::~DomElement() noexcept { } +DomElement DomElement::parse(QXmlStreamReader& reader) +{ + while (!reader.atEnd()) { + reader.readNext(); + if (reader.isStartElement()) { + break; + } + } + if (reader.hasError() || (!reader.isStartElement())) { + const QString err = "Failed to find XML root element: " + reader.errorString(); + throw std::runtime_error(err.toStdString()); + } + const DomElement root(reader); + if (reader.hasError() || (!reader.isEndElement())) { + const QString err = "XML file seems incomplete: " + reader.errorString(); + throw std::runtime_error(err.toStdString()); + } + return root; +} + +DomElement DomElement::parse(const QString& data) +{ + QXmlStreamReader reader; + reader.addData(data); + return parse(reader); +} + +DomElement DomElement::parse(const QByteArray& data) +{ + QXmlStreamReader reader; + reader.addData(data); + return parse(reader); +} + QString DomElement::getAttributeAsString(const QString& name) const { if (mAttributes.contains(name)) { @@ -37,7 +76,8 @@ QString DomElement::getAttributeAsString(const QString& name) const } } -bool DomElement::getAttributeAsBool(const QString& name) const { +bool DomElement::getAttributeAsBool(const QString& name) const +{ const QString value = getAttributeAsString(name); if (value == "yes") { return true; diff --git a/parseagle/common/domelement.h b/parseagle/common/domelement.h index 31ec234..b1cc634 100644 --- a/parseagle/common/domelement.h +++ b/parseagle/common/domelement.h @@ -3,7 +3,6 @@ #include #include -#include namespace parseagle { @@ -13,9 +12,13 @@ class DomElement final // Constructors / Destructor DomElement() = delete; - explicit DomElement(const QDomElement& root); ~DomElement() noexcept; + // Parsers + static DomElement parse(QXmlStreamReader& reader); + static DomElement parse(const QString& data); + static DomElement parse(const QByteArray& data); + // Getters const QString& getTagName() const noexcept {return mName;} const QString& getText() const noexcept {return mText;} @@ -30,6 +33,8 @@ class DomElement final private: + explicit DomElement(QXmlStreamReader& reader); + QString mName; QString mText; QHash mAttributes; diff --git a/parseagle/library.cpp b/parseagle/library.cpp index bd61281..a0927c0 100644 --- a/parseagle/library.cpp +++ b/parseagle/library.cpp @@ -33,16 +33,9 @@ Library::~Library() noexcept void Library::load(const QByteArray& content, QStringList* errors) { - QDomDocument doc; - doc.implementation().setInvalidDataPolicy(QDomImplementation::ReturnNullNode); - QString errMsg; - if (!doc.setContent(content, &errMsg)) { - throw std::runtime_error( - "Error while parsing EAGLE library: " + errMsg.toStdString()); - } - DomElement root(doc.documentElement()); - DomElement drawing = root.getFirstChild("drawing"); - DomElement library = drawing.getFirstChild("library"); + const DomElement root = DomElement::parse(content); + const DomElement drawing = root.getFirstChild("drawing"); + const DomElement library = drawing.getFirstChild("library"); load(library, errors); } diff --git a/parseagle/schematic/schematic.cpp b/parseagle/schematic/schematic.cpp index 3b2c12e..b36ba24 100644 --- a/parseagle/schematic/schematic.cpp +++ b/parseagle/schematic/schematic.cpp @@ -27,21 +27,14 @@ Schematic::~Schematic() noexcept } void Schematic::load(const QByteArray& content, QStringList* errors) { - QDomDocument doc; - doc.implementation().setInvalidDataPolicy(QDomImplementation::ReturnNullNode); - QString errMsg; - if (!doc.setContent(content, &errMsg)) { - throw std::runtime_error( - "Error while parsing EAGLE schematic: " + errMsg.toStdString()); - } - DomElement root(doc.documentElement()); - DomElement drawing = root.getFirstChild("drawing"); + const DomElement root = DomElement::parse(content); + const DomElement drawing = root.getFirstChild("drawing"); if (drawing.hasChild("grid")) { mGrid = Grid(drawing.getFirstChild("grid")); } - DomElement schematic = drawing.getFirstChild("schematic"); + const DomElement schematic = drawing.getFirstChild("schematic"); if (schematic.hasChild("description")) { mDescription = schematic.getFirstChild("description").getText(); }