Seite 1 von 1

eigener XML-Editor

Verfasst: 24. Juli 2009 00:44
von lx2
Hi Forum,

ich versuche gerade ein simplen XML-Editor zu basteln.

Dieser soll mir eine Lokale xml-Datei, die zur Steuerung einer Flash-Präsentation benutzt wird editieren.

Die xml-Datei hat einen immer wiederkehrenden Aufbau und hat eigentlich nur 3 verschiedene DOM-Elenente.
Soweit die Vorgeschichte...

Das einlesen der xml-Datei in ein QDomDocument funktioniert soweit. Das ausgeben in eine neue Datei auch.

Jetzt geht es um die Veränderung der Daten.
Ich habe gedacht, optisch wäre ein TreeWidget recht schön, das beim anklicken des jeweiligen Elementes die Eigenschaften (Knoten-Attributes) in Textfeldern anzeigt, und editieren lässt. Es soll auch möglich sein, neue Elemente einzufügen.

An die Elemente komme ich per
QDomNodeList QDomNode::childNodes () const

Leider sind die Elemente im Dom-Baum nicht eindeutig, oder hat jedes eine ID? Wie kann ich eine Löschung oder Verschiebung am besten bewerkstelligen? Ich denke beim TreeWidge könnte man den Index des angeklickten Elementes zurückgeben und ein Signal schicken, aber wie krieg ich das passende Dom-Element.

Fazit: Ich suche nach der bestmöglichen Variante die xml-Datei optisch darzustellen, und diese Darstellung direkt mit den Daten zu verknüpfen.

Vielen Dank für jede Anregung die ihr mir geben könnt!

Verfasst: 24. Juli 2009 11:21
von RHBaum
Elemente in XML haben von haus aus keine ID
Elemente innerhalb einer auflistung sind auch nicht indiziert ....
Das heisst, die reihenfolge der Elemente ist undefiniert. der parser koennte es so machen, aber auch so ...
DU solltest dein Programm so gestalten, das es dem programm auch egal ist.
Brauchst du bei deinen elementen eine ID, kannst/musst du das in deiner Applikation abhandeln ... also Konsistenzchecks oder so ...
zusaetzlich kannst Du in deinem xml einiges "sicherstellen" indem ne passende schema datei anlegst und gegen die validieren laesst.

Also in deinem Fall ....
wenn die reihenfolge deiner elemente keine bedeutung hat (fuer die praesentation) dann solltest denen auch keine bedeutung verpassen und die reihenfolge sollte dir auch schnuppe sein !

Ciao ...

Verfasst: 24. Juli 2009 12:17
von upsala
Es gibt doch in den Examples schon was ähnliches...

Verfasst: 24. Juli 2009 16:14
von lx2
@RHBaum
Das heisst, die reihenfolge der Elemente ist undefiniert. der parser koennte es so machen, aber auch so ...
Sprechen wir da von den selben Elementen?

bsp.:

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<verzeichnis>
     <titel>Wikipedia Städteverzeichnis</titel>
     <eintrag>
          <stichwort>Genf</stichwort>
          <eintragstext>Genf ist der Sitz von ...</eintragstext>
     </eintrag>
     <eintrag>
          <stichwort>Köln</stichwort>
          <eintragstext>Köln ist eine Stadt, die ...</eintragstext>
     </eintrag>
</verzeichnis>

Dann wären z.B. eintrag oder titel Elemente.

Und wenn man mit der Funktion
QDomDocument::setContent(...)
die Datei einliest wird sehr wohl die genaue Reihenfolge der Elemente beachtet!
meinDomDocument.toString() liefert also immer das gleiche Ergebniss, und das ist sehr wichtig für meine Präsentation weil ja sonst die einzelnen Folien durcheinanadergeschmissen werden.

Was der Parser nicht macht, ist die genaue Reihenfolge der Element-Atributes zu übernehmen.

ein
<title version="1.0" aktualisiert="ja" aktiv="nein">Hallo<title>
kann also schnell mal zu einem
<title aktualisiert="ja" aktiv="nein" version="1.0">Hallo<title>
werden

scheint mir aber bisher noch keine Probleme zu machen.


Ich hatte glaub ich aber gestern noch ein Denkfehler drin. Wenn ich dynamisch+rekursiv ein treeWidget auffülle, kann ich ja in jedem Tree-Item gleich eine Referenz zu seinen zugehörigen xml-Elementknoten abspeichern.
Die frage ist nur wie???


Stellen wir uns mal vor ich habe die obige xml-Datei eingelesen. Wie kann ich jetzt auf den Knoten <stichwort>Köln</stichwort> zugreifen?

Mit referenzZuKnoten.clear(); würde der sich nämlich komfortabel löschen lassen.

Das kann doch nicht sein, das der Parser das so zufällig macht, das man auf bestimmte knoten kaum zugreifen kann und sich durch den ganzen xml-Baum hangeln muss.

Verfasst: 27. Juli 2009 12:26
von RHBaum
<unbestaetigt>
die reihenfolge der elemente fasst der xerces, und damit der qt xml parser ned an. das heisst die dinger werden fast sicher in der richtigen reihenfolge im parser kommen.
Das ist aber kein muss. andere parser keonnten die reihenfolge aendern. und haetten damit kein falsches verhalten.
</unbestaetigt>

Ohne irgendwelche indikatoren ist bei XML die reihenfolge damit eigentlich wertungssfrei.

wenn die reihenfolge bei dir eine rolle spielt, und das tut sie ja, wuerd ich auch empfehlen ein "Position" attribut mit einzufuegen ....

Wenn du z.b. schon einen node mit ner anzahl an childs hasst, hasst du eigentlich keine chance, ein weiteres element an einer ganz bestimmten Position anzufuegen. Du kannst es nur generell anfuegen, und deine Impl entscheidet, wo sie das ding hinschreibt (meist ans ende) ....
Das kann natuerlich nur funktionieren, wenn die reihenfolge wertungsfrei ist ....
waere die Reihenfolge nicht wertungsfrei, wuerde appendchild noch nen parameter benoetigen, der bestimmt an welcher position das element dann steht .... und es wuerde sicher auch move methoden fuer elemente auf gleicher ebene geben ....

Das ist nun mal "das Wesen" von XML ....

Aber lies dazu lieber die DOM spezifikation .... QDom glaub ich, implementiert bissi mehr als DOM vorschreibt ...
Wie kann ich jetzt auf den Knoten <stichwort>Köln</stichwort> zugreifen?
Die bittere Antwort:
ueber alle elemente iterieren, und die attribute checken ^^
der XML parser nimmt einem da nix ab.

Brauchst du so Zugriffe oefters, QMap<QString,QDomNode * > ist gar kein so ungewoehnliches Gebilde in dem zusammenhang ^^

Nen (hirarisches)Modell aus ner XML struktur zu schreiben, ist auch ne gute fingeruebung, und gar ned so schwer ^^ Aber dazu gibts auch dutzende scho fertige implementierungen, die man fuer ne eigene Loesung gut zu rate ziehen kann ....

Trotz parser muss man bei xml eben doch noch ne menge selbst machen, dafuer iss der parser und xml eben flexibel ende nie !

Ciao ....

xml knoten überhaupt erst mal löschen

Verfasst: 30. Juli 2009 00:03
von lx2
irgendwie scheitere ich gerade noch grundlegender :(


Ich habe eine XML mit QDomDocument.setContent(file) geladen.

weder

Code: Alles auswählen

QDomDocument.removeChild(QDomDocument.documentElement().firstChild());
noch

Code: Alles auswählen

QDomDocument.documentElement().firstChild().clear();
löschen den Knoten. Mir kommt zwar vor das er temporär gelöscht wird,
beim Speichern ist er aber einfach wieder da.

Gespeichert hab ich mit
TextStream << QDomDocument.toString() oder aber mit QDomDocument.save(TextStream)

Komischerweise funktioniert

Code: Alles auswählen

QDomDocument.removeChild(QDomDocument.documentElement()
Da bleibt dann von der XML nur die XML-Deklaration übrig, also
<?xml version='1.0' encoding='utf-8' ?>

Verfasst: 30. Juli 2009 12:17
von RHBaum
Müsste man mehr Code zu sehen ....

Aber noch mehr grundlegende Dinge:

Das DOM Modell iss nen Moloch, was den speicherverbrauch angeht. Naja, XML blaeht die Infos ja auch gehoerig auf :-)

Eigentlich ist die haufigste vorgehensweisse bei der arbeit mit XML:
Dom Struktur einlesen, Dom Struktur in die eigene Datenstruktur ueberfuehren (konverter) Dom struktur gleich wieder wegwerfen (deleten, speicher freigeben). Zum speichern dann aequivalent : DomStructur komplett neu aus der eigenen datenstruktur erzeugen, in den STream schreiben, DomStructur gleich wieder wegwerfen.

Hat den vorteil: Man arbeitet auf nem eigenen Datenmodell, was um laengen performanter und effizienter ist.
Das eigene Datenmodell kann viel mehr abstrahieren, also ne intuitivere Schnittstelle zu den daten bereitstellen.
Es ist viel einfacher, dann aenderungen in der XML Structur vorzunehmen, oder gar statt XML andere Formate zur serialisierung anzubieten.

Ob man es braucht, muss man selbst entscheiden ...

gewoeh dir das hin und hergerutsche mit den Strings ab ^^
Wenn deine XML Structuren mal etwas groesser werden, fliegt dir der QString mal um die ohren ... ausserdem verschwendest du viel performance ... was ned sein muss.
Arbeite mit den QDomElementen direkt ....

Code: Alles auswählen

QDomDocument doc; 
QTextStream out;
/// 
doc.save(out); 
Sollte der bevorzugte weg sein, nen XML rauszuschreiben.
QDomDocument.documentElement().firstChild().clear();
mit clear wandelst du einen typisierten Node in einen untypisiertem, also einen NULL node um ....
Ich nehm an, das funktioniert ned, wenn der node selber noch childs hat ...

Versuch mit QDomNode::removeChild die knoten aus der struktur auszuhaengen .... das geht IMHO aber auch nur, wenn die der knoten scho leer iss ....

Aber probier mal .... notfalls musst du halt vorher rekursiv alle unterknoten loeschen.

der removte knoten existiert zwar immer noch im dom dann ... wird aber von der struktur nimmer referenziert, iss also von ausen wie geloescht, und sollte beim serialisieren nimmer mitgeschrieben werden. Ausm Speicher wird er aber erst geworfen, nachdem das QDomDocument zerstoert hasst.

Wie gesagt, ich hab das nie gebraucht, da ich immer auf der eigenen datenstruktur arbeite und nen DOM wenn ueberhaupt, nur kurz zum serialisieren erzeuge ....

Ciao ....

Verfasst: 31. Juli 2009 10:03
von lx2
Hast wohl recht mit der eigenen Dateistruktur - Der Aufwand ist es aber nicht Wert. Im Prinzip geht es bei mir um eine XML-Datei die für eine Präsentation herhalten muss. Sie hat jedes Jahr ca um die 50 Knoten(mit wenigen Unterknoten), und wird von zig Mitarbeitern bearbeitet. Weil die keine Ahnung vom xml-Standart haben, kommt es hin und wieder vor dass die Datei "verpfuscht wird" und man dann immer suchen muss wo ein "/>" fehlt oder Anführungszeichen vergessen worden ist.
Deswegen wollte ich einen kleinen Editor basteln, damit sichergestellt wird das die XML zur steuerung der Flash-Datei immer valide ist.

DOM requires more memory. The entire document is to be kept in memory. This gives in return the ability to modify and work with the document freely in memory and then put it back to the disk. This is very handy in many situations.
... xml ist ja eigentlich genau dafür gemacht, einfach und direkt Änderungen durchzuführen, deswegen will ich auch direkt auf dem DOM arbeiten. In nem englischen Forum hab ich das mit dem Speicherproblem auch schon gelesen. Da bläht ne 50MB xml-Datei sich im RAM auf 2GB(!!!) auf. Die dortige Löung war die Implementation von einem OpenSource XML Editor für Linux herzunehmen/umzuschreiben.
http://lists.trolltech.com/qt-interest/ ... 393-0.html
Meine xml ist so ca 30 KB.

---
Zurück zu meinem Problem:
"Wer lesen kann ist klar im vorteil" - Dieses Sprichwort hat bei mir wieder vollkommen zugeschlagen. In der Doku bin ich heute nochmal drübergestolpert:
QDomNode QDomNode::removeChild ( const QDomNode & oldChild )

Removes oldChild from the list of children. oldChild must be a direct child of this node.

Returns a new reference to oldChild on success or a null node on failure.
Ist klar wenn man irgendwie versucht Childelemente zu löschen mit z.B.
domDocument.removeChild("unterelementvomunterelement") es nicht geht, weil das vom Papa aus gelöscht werden muss ;) .

Find ich ja auch irgendwie ganz niedlich das nur Papa das darf, macht die Sache aber wieder um einiges komplexer. Rekursion ist wohl unumgänglich, wobei ich doch eher der Iterationsfreund bin...

P.S.:

hatte ja oben schon die QDomDocumnet.save(Textstream) Methode verwendet. Hab auch festgestellt das die wesentlich besser ist, weil da mein Input wenigstens einigermaßen formatiert wieder rauskommt.

Verfasst: 31. Juli 2009 11:03
von RHBaum
... xml ist ja eigentlich genau dafür gemacht, einfach und direkt Änderungen durchzuführen, deswegen will ich auch direkt auf dem DOM arbeiten.
Nein ! ^^ XML ist nur das Protokoll. DOM ist dafuer gemacht !
DOM eignet sich super, wenn man die daten aus dem XML immer vorhalten woll und mit den korrespondierenden Objecten gleich synchronisieren lassen will. Dann hat man nen Datenbank aehnliches Prinzip. Das faellt einem oft leichter.

Aber in der Praxis funktioniert das eben nur eingeschraenkt. die xml dateien duerfen ned wirklich gross sein, und vieles veraendern blaeht den internen cache der dom implementation meist unendlich auf und fuehrt zu Performanceproblemen.

Wobei ich denke das du an die grenzen ned kommen wirst .
Find ich ja auch irgendwie ganz niedlich das nur Papa das darf, macht die Sache aber wieder um einiges komplexer.
Noe, wieso ???
Das element das loeschen willst brauchst soweiso ....
und den papa bekommst doch ganz einfach mit parentNode() !
Rekursion ist wohl unumgänglich, wobei ich doch eher der Iterationsfreund bin...
Versuch erst mal ohne die childs zu loeschen ... vielleicht haengt er einfach den ganzen baum dann aus .... Das wuerde dir die rekursion sparen.

DOM Typisches Problem ist halt, das die ganzen geloeschten knoten als leichen in deiner Dom struktur Umheroxidieren.
Wenn das parsen ned allzulange dauert, kann man ja nen reloadbutton oder so einbauen, wo er das zeugs fix in ne temp datei streamt, du dein urspruengliches dom document zerstoerst und wieder erzeugst .... und vom temp aus parst.

Alternativ kannst auch die daten von dem alten DOMDoc in ein neues kopieren, die leichen sollten dann ned beruecksichtigt werden. deiner verarbeitung musst dann halt das neue doc unterschieben und alles neu synchroniseren / aufbauen ....

Ansonsten, bekommst halt ne App im guten alten Winword style ^^ alle 2h speichern, sonst fliegt einem eh alles um die ohren ^^

Ciao ...