210 lines
6.2 KiB
C++
210 lines
6.2 KiB
C++
|
|
|
||
|
|
|
||
|
|
#include "ntxnodexml.h"
|
||
|
|
#include <ntxnodefactory.h>
|
||
|
|
#include <pugixml.hpp>
|
||
|
|
#include <iostream>
|
||
|
|
#include <sstream> // Add this include at the top of the file
|
||
|
|
|
||
|
|
|
||
|
|
namespace ntx
|
||
|
|
{
|
||
|
|
// Rekursive Hilfsfunktion
|
||
|
|
static NtxNodePtr parseElementRecursive(const pugi::xml_node& xmlNode)
|
||
|
|
{
|
||
|
|
if (!xmlNode)
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
// 1. Tag-Name als ID-String verwenden
|
||
|
|
NtxString tagName = xmlNode.name();
|
||
|
|
|
||
|
|
// Ignoriere spezielle Knotentypen (Kommentare, PCDATA/Text, etc.) wenn sie leer sind
|
||
|
|
// In diesem einfachen Beispiel behandeln wir nur Element-Knoten als echte NtxNodes.
|
||
|
|
if (xmlNode.type() != pugi::node_element)
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
// 2. Erzeuge Knoten via Factory (nutzt implizit CTypeFactory für Mapping)
|
||
|
|
NtxNodePtr newNode = NtxNodeFactory::makeNode(tagName);
|
||
|
|
if (!newNode)
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
// 3. Attribute übertragen
|
||
|
|
for (pugi::xml_attribute attr : xmlNode.attributes())
|
||
|
|
{
|
||
|
|
NtxString key = attr.name();
|
||
|
|
|
||
|
|
// Einfache Heuristik für Typen:
|
||
|
|
// PugiXML liefert strings. Wir könnten versuchen zu konvertieren oder alles als String lassen.
|
||
|
|
// Hier: Alles als String speichern, oder versuchen int/double zu parsen.
|
||
|
|
// Fürs erste: Speichern als String, Payload erlaubt mixed types.
|
||
|
|
newNode->set(key, NtxString(attr.value()));
|
||
|
|
|
||
|
|
/* Optional: Intelligentes Parsen
|
||
|
|
if (attr.as_int() != 0 || std::string(attr.value()) == "0")
|
||
|
|
newNode->set(key, attr.as_int());
|
||
|
|
else
|
||
|
|
newNode->set(key, NtxString(attr.value()));
|
||
|
|
*/
|
||
|
|
}
|
||
|
|
|
||
|
|
// 4. Kinder rekursiv verarbeiten
|
||
|
|
for (pugi::xml_node child : xmlNode.children())
|
||
|
|
{
|
||
|
|
NtxNodePtr childNode = parseElementRecursive(child);
|
||
|
|
if (childNode)
|
||
|
|
{
|
||
|
|
newNode->addChild(childNode);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return newNode;
|
||
|
|
}
|
||
|
|
|
||
|
|
NtxNodePtr NtxNodeXml::loadFromFile(const std::string& filename)
|
||
|
|
{
|
||
|
|
pugi::xml_document doc;
|
||
|
|
pugi::xml_parse_result result = doc.load_file(filename.c_str());
|
||
|
|
|
||
|
|
if (!result)
|
||
|
|
{
|
||
|
|
std::cerr << "XML Load Error: " << result.description() << " in " << filename << std::endl;
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Wir nehmen den ersten Root-Knoten (das Wurzelelement)
|
||
|
|
// doc.document_element() gibt das erste Kind vom Typ Element zurück (ignoriert PI, DOCTYPE etc.)
|
||
|
|
return parseElementRecursive(doc.document_element());
|
||
|
|
}
|
||
|
|
|
||
|
|
NtxNodePtr NtxNodeXml::loadFromString(const std::string& xmlContent)
|
||
|
|
{
|
||
|
|
pugi::xml_document doc;
|
||
|
|
pugi::xml_parse_result result = doc.load_string(xmlContent.c_str());
|
||
|
|
|
||
|
|
if (!result)
|
||
|
|
{
|
||
|
|
std::cerr << "XML Parse Error: " << result.description() << std::endl;
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
return parseElementRecursive(doc.document_element());
|
||
|
|
}
|
||
|
|
|
||
|
|
// Rekursive Hilfsfunktion zum Serialisieren
|
||
|
|
static void serializeNodeRecursive(const NtxNodeCPtr& node, pugi::xml_node& xmlNode)
|
||
|
|
{
|
||
|
|
if (!node)
|
||
|
|
return;
|
||
|
|
|
||
|
|
// 1. Tag-Name aus CType-Label holen
|
||
|
|
NtxString tagName = node->getClassTypeLabel();
|
||
|
|
pugi::xml_node childElement = xmlNode.append_child(tagName.c_str());
|
||
|
|
|
||
|
|
// 2. Alle Attribute aus dem Payload schreiben
|
||
|
|
node->forEachProperty([&childElement](const NtxString& key, const NtxVariant& value)
|
||
|
|
{
|
||
|
|
// Konvertiere NtxVariant zu String und setze als XML-Attribut
|
||
|
|
std::visit([&childElement, &key](auto&& arg)
|
||
|
|
{
|
||
|
|
using T = std::decay_t<decltype(arg)>;
|
||
|
|
|
||
|
|
if constexpr (std::is_same_v<T, std::monostate>)
|
||
|
|
{
|
||
|
|
// Überspringe leere Werte
|
||
|
|
}
|
||
|
|
else if constexpr (std::is_same_v<T, NtxString>)
|
||
|
|
{
|
||
|
|
childElement.append_attribute(key.c_str()).set_value(arg.c_str());
|
||
|
|
}
|
||
|
|
else if constexpr (std::is_same_v<T, int>)
|
||
|
|
{
|
||
|
|
childElement.append_attribute(key.c_str()).set_value(arg);
|
||
|
|
}
|
||
|
|
else if constexpr (std::is_same_v<T, double>)
|
||
|
|
{
|
||
|
|
childElement.append_attribute(key.c_str()).set_value(arg);
|
||
|
|
}
|
||
|
|
else if constexpr (std::is_same_v<T, bool>)
|
||
|
|
{
|
||
|
|
childElement.append_attribute(key.c_str()).set_value(arg);
|
||
|
|
}
|
||
|
|
}, value);
|
||
|
|
});
|
||
|
|
|
||
|
|
// 3. Kinder rekursiv verarbeiten
|
||
|
|
for (size_t i = 0; i < node->getChildCount(); ++i)
|
||
|
|
{
|
||
|
|
NtxNodeCPtr child = node->getChild(i);
|
||
|
|
if (child)
|
||
|
|
{
|
||
|
|
serializeNodeRecursive(child, childElement);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool NtxNodeXml::saveToFile(const NtxNodePtr& node, const std::string& filename)
|
||
|
|
{
|
||
|
|
return saveToFile(std::const_pointer_cast<const NtxINode>(node), filename);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool NtxNodeXml::saveToFile(const NtxNodeCPtr& node, const std::string& filename)
|
||
|
|
{
|
||
|
|
if (!node)
|
||
|
|
{
|
||
|
|
std::cerr << "Cannot save null node to file: " << filename << std::endl;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
pugi::xml_document doc;
|
||
|
|
|
||
|
|
// XML-Deklaration hinzufügen
|
||
|
|
pugi::xml_node declaration = doc.prepend_child(pugi::node_declaration);
|
||
|
|
declaration.append_attribute("version") = "1.0";
|
||
|
|
declaration.append_attribute("encoding") = "UTF-8";
|
||
|
|
|
||
|
|
// Root-Node serialisieren
|
||
|
|
serializeNodeRecursive(node, doc);
|
||
|
|
|
||
|
|
// In Datei speichern mit Formatierung
|
||
|
|
bool success = doc.save_file(filename.c_str(), " ", pugi::format_default, pugi::encoding_utf8);
|
||
|
|
|
||
|
|
if (!success)
|
||
|
|
{
|
||
|
|
std::cerr << "Failed to save XML to file: " << filename << std::endl;
|
||
|
|
}
|
||
|
|
|
||
|
|
return success;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string NtxNodeXml::saveToString(const NtxNodePtr& node)
|
||
|
|
{
|
||
|
|
return saveToString(std::const_pointer_cast<const NtxINode>(node));
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string NtxNodeXml::saveToString(const NtxNodeCPtr& node)
|
||
|
|
{
|
||
|
|
if (!node)
|
||
|
|
{
|
||
|
|
std::cerr << "Cannot serialize null node to string" << std::endl;
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
|
||
|
|
pugi::xml_document doc;
|
||
|
|
|
||
|
|
// XML-Deklaration hinzufügen
|
||
|
|
pugi::xml_node declaration = doc.prepend_child(pugi::node_declaration);
|
||
|
|
declaration.append_attribute("version") = "1.0";
|
||
|
|
declaration.append_attribute("encoding") = "UTF-8";
|
||
|
|
|
||
|
|
// Root-Node serialisieren
|
||
|
|
serializeNodeRecursive(node, doc);
|
||
|
|
|
||
|
|
// In String konvertieren
|
||
|
|
std::ostringstream oss;
|
||
|
|
doc.save(oss, " ", pugi::format_default, pugi::encoding_utf8);
|
||
|
|
|
||
|
|
return oss.str();
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace ntx
|