xml parser schreiben, wie Format und ausgabe gestalten?
-
will einfach mal üben, aber ich mach das immer unter dem aspekt "wie würden fremde leute damit am einfachsten arbeiten können". macht die sache zwar nicht leichter, aber imho lernt man mehr dabei.
-
sorry, ich hab nicht alles gelesen.
nur schoen vom anwenden faend ich sowas:XmlElement e = Parse("abc.xml"); try { int id = e["Datensatz"]["id"]; string kunde = e["kunde"].find("nummer",1337); } catch (ElementNotFoundException& e) { // ... }
-
so nicht zu machen, weil der op[] keine unterschiedlichen rückgabetypen haben kann.
was vielleicht ginge wär sowas:
int id=e.find("Datensatz").find<int>("id");
zwar nichtmehr ganz soschön zu lesen, aber ginge.
ist imho aber auch ein völlig anderer ansatz, weil parse dann einen Baum erstellt, der hinterher durchsucht wird.Mein ziel war es aber, dass das Parse ergebnis eben kein Baum ist, sondern wenn möglich schon die komplett ausgewerteten Datensätze ausspuckt(wenn der user tuple akzeptieren würde, könnte man ja noch weitergehen, und parse soweit ausbauen, dass die tuples gleich mithilfe einer vom user geschriebenen Funktion umgewandelt werden...oh das wär schön...).
Ein Traum wär dann zb sowas:
//abc xml enthält viele Datensätze std::vector<Datensatz> ergebnis=parse<Datensatz>("abc.xml",Format);
-
Wäre vielleicht ganz hilfreich die Rahmenbedingungen noch ein wenig besser darzustellen.
Geht's nur um Datensatzdateien im XML-Format? Ohne Querverweise?
Wie sieht's hiermit aus:<Kunde> <Nummer>1337</Nummer> <Vorname>Max</Vorname> <Nachname>Mustermann</Nachname> <Adresse> <Strasse>Musterstrasse</Strasse> <Plz>1337357</Plz> <Ort>Munster</Ort> </Adresse> </Kunde>
Damit zusammenhängend natürlich die Frage wie's mit eigenen Datentypen in XML aussieht?
Wenn's nur PODs und ein paar STL-Container gibt, was spricht dagegen aus dem struct Datensatz ein Objekt zu machen auf das man assoziativ zugreifen kann? Oder zu viel Overhead?
Und wenn's tatsächlich nur um solche Datensätze geht würde ich das uU eher als SAX-Aufsatz oder so implementieren.
(Ein Framework um simpel gleich richtige Objekte zu erzeugen wäre auch nicht schlecht
)
-
otze schrieb:
so nicht zu machen, weil der op[] keine unterschiedlichen rückgabetypen haben kann.
Wäre eventuell eine Möglichkeit boost::any einzusetzen? Aber dann muss blöd rumgecastet werden...
-
die rahmenbedingungen sind folgende:
Dein Beispiel ist das maximale was in der Version 1.0 geparst werden können soll. halt so primitiv wie möglich. was ich in einer version 2.0 mache steht noch in den sternen(wenns sie denn überhaupt gibt)^^
was vielleicht noch dazukommt ist etwas, dass es erlaubt, dass bestimmte tags mehrfach vorkommen(zweit und drittwohnsitz oder mehrere telefonnummern etc), aber da würde das tuple prinzip wohl schon den geist aufgeben, weil die typen im rückgabewert einfach zu komplex werden(eine einzelne telefonnummer ist ein int, mehrere sind ein vector<int>,mehrere addressen währen dann ein
vector<Tuple<Typlist<string,Typlist<int,Typlist<string,Nulltype> > > > >
Jaa freude
Wenn's nur PODs und ein paar STL-Container gibt, was spricht dagegen aus dem struct Datensatz ein Objekt zu machen auf das man assoziativ zugreifen kann? Oder zu viel Overhead?
versteh grad nicht, was du meinst.
Datensatz aus meinen Beispielen soll ein vom user definierter typ sein, der dann auch hintrher im programm so benutzt wird.Walli schrieb:
otze schrieb:
so nicht zu machen, weil der op[] keine unterschiedlichen rückgabetypen haben kann.
Wäre eventuell eine Möglichkeit boost::any einzusetzen? Aber dann muss blöd rumgecastet werden...
dann könnte ich die zahlen etc auch gleich in ihrer rohform halten und hinterher durchn stringstream schicken, das problem ist bei allen möglichkeiten, dass man immer im blick haben muss, was welcher typ ist, und das behagt mir garnicht.
-
otze schrieb:
Dein Beispiel ist das maximale was in der Version 1.0 geparst werden können soll. halt so primitiv wie möglich. was ich in einer version 2.0 mache steht noch in den sternen(wenns sie denn überhaupt gibt)^^
Naja, wenn ein Datensatz einen Datensatz enthalten kann dann sollte dieser wohl auch recht problemlos noch einen weiteren enthalten können, oder? (Meine vom Aufwand her, da dürfte wohl hauptsächlich das ob überhaupt eine Rolle spielen.)
Oder meintest du Adresse als "eigener" Datentyp?otze schrieb:
was vielleicht noch dazukommt ist etwas, dass es erlaubt, dass bestimmte tags mehrfach vorkommen
Eine Liste?
otze schrieb:
eine einzelne telefonnummer ist ein int
Eine Telefonnummer ist ein string oder vector<int>
Was ich vorhin vergessen hab:
otze schrieb:
<list typ="float" separator="," name="werte">15.5,16.7,17.9</list>
Das sieht ziemlich übel aus - wie wär's mit
<list typ="float" name="werte"> <e>15.5</e> <e>16.7</e> <e>17.9</e> </list>
-
also, ich fang nochmal von vorn an(hab euch leider mit meinen gedankensprüngen verwirrt)
die version 1.0 wird folgenden Ausdruck unterstützen:
<Foo>Bar</Foo>
Foo ist ein beliebiger String
Bar ist entweder ein Wert oder wieder so ein Ausdruck(also ist endlosverschachtelung erlaubt).mehr darf die XML datei nicht enthalten, damit mein Parser keine exception wirft ;).
Dem Parser wird erstmal ein string oder ein iteratorpaar und ein Format übergeben. Dazu besitzt Parser noch einen template parameter(dazu später mehr).
Dass Format soll die Form der XML datei angeben.
zwar ist syntaxmäßig noch nichts entschieden, aber das ganze könnte zb so aussehen:
Datei:
<Kunde> <Nummer>1337</Nummer> <Vorname>Max</Vorname> <Nachname>Mustermann</Nachname> </Kunde>
ausruck in C++
//ich nenn den Ausdruck <Foo>Bar</Foo> einfach mal Tag Tag("Kunde").inner( Tag("Nummer").inner(Integer()), Tag("Vorname").inner(String()), Tag("Nachname").inner(String()) )
wenn ein Datensatz mehrfach vorkommen kann, wird es vielleicht möglich sein durch benutzen des op * dies anzudeuten(anleihe aus spirit)
eine liste die floats annimmt, könnte vielleicht dann mit folgendem ausdruck dargestellt werden:
Tag("list").inner( *Tag("e").inner(Real()) )
Wenn die eingabe mit diesem Format nicht übereinstimmt, wird ne exception geworfen, sonst passiert folgendes:
(der templateparameter der funktion wird hier T genannt)
-es wird ein tuple erstellt und gefüllt. Das Tuple richtet sich nach dem Format
-das Tuple wird einer convert<T> funktion übergeben.
-die convert<T> funktion mach aus dem Tuple ein T
-T wird zurückgegeben
(und ab hier wirds gänzlich unausgegoren:)
-wenn der "höchste" Parser(also der der ganz links im Format steht) ein op* ist, tritt ein sonderfall auf
-der rückgabewert der funktion ist ein std::vector<T>
-die funktion convert<T> wird für jeden verfügbaren "Datensatz" aufgerufen und T dann in einen vector inserted
-der vector wird zurückgegeben
im endeffekt ist die sache mit der rückgabe und konvertierung ziemlich mies, aber ich lass mir gerne da reinreden
-
(Ohne mir das jetzt alles durchgelesen zu haben.)
Warum nimmst du als Format nicht einfach einen ifstream, der auf einen dtd zeigen sollte? Validierung ist was feines, da kümmert sich der Endanwender unger drum...
Als ausgabe könnte ich mir folgendes vorstellen:
Erstmal legen wir folgendes fest:
-es gibt elemente, diese können Attribute haben. Attribute speichern wir in stringform
-Elemente können entweder PCDATA (strings) oder andere Elemnte enthalten (wen interessieren kommentare und processing instructions)
Dann würden mir folgende Lösungen einfallen:
1.: du machst einen sequenziellen Parser (also eine abstrakte Parserklasse, von der man ableiten und virtuelle Funktionen á la begin_element überschreiben muss)
2.: du gibst das als ganzes Zurück:struct element { enum type{pcdata,element}; type t; union { std::vector<element> v; std::string p; }data; };
Ich schätze mal, das steht spirit beides im wege.
Oder du machst einen auf 1337 und lässt ein statisches, templatebasiertes Format zu. Da dürfte die Benutzung dann aber mehr oder weniger unmöglich werden...
-
Oder du machst einen auf 1337 und lässt ein statisches, templatebasiertes Format zu. Da dürfte die Benutzung dann aber mehr oder weniger unmöglich werden...
wieso unmöglich? wenn zur compilezeit alles über das format bekannt ist, ist das doch ok?
Ich schätze mal, das steht spirit beides im wege.
spirit kann das. spirit bietet 2 arten der verarbeitung: als baum nach dem parsen oder während des parsens mittels closures und lambda ausdrücken/functoren. da man relativ gut erstellen kann, was in den baum reinkommt bzw die lambda ausdrücke höchst mächtig sind, geht das klar.