Hallo Gemeinde!
Ich bastle gerade an einem XML-Parser, der mir eine XML Datei einliest.
Am anfang habe ich mich mit libxml rumgeschlagen... das hat eigentlich auch funktioniert. Aber irgendwie ist das nicht ganz C++ Konform, weswegen ich dann auf libxml++ umgestiegen bin.
Meine beiden Beispiele, habe ich beide über die Examples der beiden libs gebaut.
Nur bei der libxml++ steig ich gerade nicht mehr durch.
Hier der kl. Teil worum es geht:
Ich habe eine Funktion parseNode die beim ersten Aufruf den Node und eine Zahl übergeben bekommt. Die Funktion ruft sich selbst wieder auf (rekursiv) bis sie alle Nodes durch hat.
Bei der libxml ist das ganze eigentlich sehr ähnlich wie bei der libxml++, nur mit dem unterschied, dass ich bei der libxml nur bei einem child die funktion rekursiv aufrufe.
Bei der libxml++ ist das irgendwie auch so, nur versteh ichs eben nicht.
Hier mal das Beispiel mit der libxml
void XmlMenu::parseNode(xmlNode *a_node, int Parent)
{
xmlNode *cur_node = NULL;
int MainMenuIndex, myMenuNr;
MainMenuIndex = 0;
myMenuNr=MenuNr;
MenuNr++;
for (cur_node = a_node; cur_node; cur_node = cur_node->next)
{
if (cur_node->type == XML_ELEMENT_NODE)
{
for(int i=0;i<=Parent;i++)
{
printf(" ");
}
if (xmlStrcmp(cur_node->name, (const xmlChar*) "menus")==0)
{
printf("%d-%d-%d-ROOT\n", Parent, MainMenuIndex, myMenuNr);
}
else if (xmlStrcmp(cur_node->name, (const xmlChar*) "system")==0)
{
printf("%d-%d-%d-SystemItem=%s\n", Parent, MainMenuIndex, myMenuNr, (const char*) xmlGetProp(cur_node, (xmlChar*) "name"));
MainMenuIndex++;
}
else if (xmlStrcmp(cur_node->name, (const xmlChar*) "menu")==0)
{
printf("%d-%d-%d-MenuItem=%s\n", Parent, MainMenuIndex, myMenuNr, (const char*) xmlGetProp(cur_node, (xmlChar*) "name"));
MainMenuIndex++;
}
else if (xmlStrcmp(cur_node->name, (const xmlChar*) "plugin")==0)
{
printf("%d-%d-%d-PluginItem=%s\n", Parent, MainMenuIndex, myMenuNr, (const char*) xmlGetProp(cur_node, (xmlChar*) "name"));
// TODO: Check if Plugin available, when not - dont count MainMenuIndex
MainMenuIndex++;
}
//addItem();
//printf("name: %s | properties: %s\n", cur_node->name, xmlGetProp(cur_node,"name"));
}
parseNode(cur_node->children, Parent+1);
}
}
Alles anzeigen
Wenn ich die XML-Datei damit einlesen, schaut die Ausgabe so aus:
0-0-0-ROOT
1-0-1-SystemItem=Schedule
1-1-1-SystemItem=Channels
1-2-1-SystemItem=Timers
1-3-1-SystemItem=Recordings
1-4-1-MenuItem=Video/Audio
2-0-11-PluginItem=radio
2-1-11-PluginItem=playlist
2-2-11-PluginItem=mp3
2-3-11-PluginItem=mplayer
2-4-11-MenuItem=CD/DVD
3-0-21-PluginItem=burn
3-1-21-PluginItem=dvdselect
3-2-21-PluginItem=dvd
3-3-21-PluginItem=vdrcd
3-4-21-PluginItem=vcd
1-5-1-MenuItem=Suche/Info
2-0-35-PluginItem=epgsearch
2-1-35-PluginItem=pilot
2-2-35-PluginItem=newsticker
2-3-35-PluginItem=tvtv
2-4-35-PluginItem=tvonscreen
2-5-35-PluginItem=yaepg
2-6-35-PluginItem=osdpip
2-7-35-PluginItem=timeline
2-8-35-PluginItem=osdteletext
2-9-35-PluginItem=prefermenu
2-10-35-PluginItem=director
Alles anzeigen
Man sieht: Die erste Zahl zeigt mir sozusagen die Tiefe an, dann die zweite Zahl die Nummerierung in jedem einzelnen Menü und die dritte Zahl sollte eigentlich die Nummer des Menüs darstellen, nur hatte ich an der stelle dann auf die libxml++ umgeschwenkt (ist auch nicht wichtig, das würde ich ja noch hinbekommen)
So jetzt kommen wir zum umbau zu libxml++. Hier schaut die Funktion dann so aus:
void XmlMenu::parseNode(const Node* a_node, unsigned int Parent)
{
const ContentNode* nodeContent = dynamic_cast<const ContentNode*>(a_node);
const TextNode* nodeText = dynamic_cast<const TextNode*>(a_node);
const CommentNode* nodeComment = dynamic_cast<const CommentNode*>(a_node);
int MainMenuIndex, myMenuNr;
MainMenuIndex = 0;
myMenuNr=MenuNr;
MenuNr++;
Glib::ustring nodename = a_node->get_name();
if(!nodeText && !nodeComment && !nodename.empty()) //Let's not say "name: text".
{
for(int i=0;i<=Parent;i++)
{
printf(" ");
}
if (nodename == "menus")
{
cout << Parent << "-" << MainMenuIndex << "-" << myMenuNr << "-ROOT" << endl;
}
else if (nodename == "system")
{
if (const Element* nodeElement = dynamic_cast<const Element*>(a_node))
{
const Element::AttributeList& attributes = nodeElement->get_attributes();
for(xmlpp::Element::AttributeList::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter)
{
const Attribute* attribute = *iter;
cout << Parent << "-" << MainMenuIndex << "-" << myMenuNr << "-SystemItem=" << attribute->get_value() << endl;
}
MainMenuIndex++;
}
}
else if (nodename == "menu")
{
if (const Element* nodeElement = dynamic_cast<const Element*>(a_node))
{
const Element::AttributeList& attributes = nodeElement->get_attributes();
for(xmlpp::Element::AttributeList::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter)
{
const Attribute* attribute = *iter;
cout << Parent << "-" << MainMenuIndex << "-" << myMenuNr << "-MenuItem=" << attribute->get_value() << endl;
}
}
MainMenuIndex++;
}
else if (nodename == "plugin")
{
if (const Element* nodeElement = dynamic_cast<const Element*>(a_node))
{
const Element::AttributeList& attributes = nodeElement->get_attributes();
for(xmlpp::Element::AttributeList::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter)
{
const Attribute* attribute = *iter;
cout << Parent << "-" << MainMenuIndex << "-" << myMenuNr << "-PluginItem=" << attribute->get_value() << endl;
}
}
MainMenuIndex++;
}
}
if(!nodeContent)
{
//Recurse through child nodes:
Node::NodeList list = a_node->get_children();
for(Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
{
parseNode(*iter, Parent+1); //recursive
}
}
}
Alles anzeigen
(ich weiss, das könnte man noch schöner machen, aber zur Optimierung komme ich dann noch, wenns geht)
So, und hier ist die Ausgabe dann folgende:
0-0-0-ROOT
1-0-2-SystemItem=Schedule
1-0-4-SystemItem=Channels
1-0-6-SystemItem=Timers
1-0-8-SystemItem=Recordings
1-0-10-MenuItem=Video/Audio
2-0-12-PluginItem=radio
2-0-14-PluginItem=playlist
2-0-16-PluginItem=mp3
2-0-18-PluginItem=mplayer
2-0-20-MenuItem=CD/DVD
3-0-22-PluginItem=burn
3-0-24-PluginItem=dvdselect
3-0-26-PluginItem=dvd
3-0-28-PluginItem=vdrcd
3-0-30-PluginItem=vcd
1-0-34-MenuItem=Suche/Info
2-0-36-PluginItem=epgsearch
2-0-38-PluginItem=pilot
2-0-40-PluginItem=newsticker
2-0-42-PluginItem=tvtv
2-0-44-PluginItem=tvonscreen
2-0-46-PluginItem=yaepg
2-0-48-PluginItem=osdpip
2-0-50-PluginItem=timeline
2-0-52-PluginItem=osdteletext
2-0-54-PluginItem=prefermenu
2-0-56-PluginItem=director
Alles anzeigen
Wie man hier sieht, funktioniert die Tiefenzählung (erste Zahl). Aber da sitzt genau mein verständnisproblem! Denn in diese Funktion wird !! pro eintrag !! aufgerufen. Was man auch schön an der zweiten Zahl sehen kann, denn diese ist immer 0.
Kann mir jemand erklären, wie das funktioniert? Warum funktioniert die Zählung der Ebene (erste Zahl).
Theoretisch ist es doch so, dass die Funktion beim aufruft hingeht und schaut ob sie eine der Zeilen zugeordnet werden kann und gibt entsprechend dann die Ausgabe aus.
Als nächstes ruft diese sich dann so lange wieder auf, bis alles notes durch sind. Dabei sollte aber jeder aufruf (was einer Zeile ausgabe entspricht!) die übergebene Zahl hochzählen...
also sollte die Ausgabe doch so aussehen!
0-0-0-ROOT
1-0-2-SystemItem=Schedule
2-0-4-SystemItem=Channels
3-0-6-SystemItem=Timers
4-0-8-SystemItem=Recordings
5-0-10-MenuItem=Video/Audio
6-0-12-PluginItem=radio
7-0-14-PluginItem=playlist
8-0-16-PluginItem=mp3
9-0-18-PluginItem=mplayer
10-0-20-MenuItem=CD/DVD
Alles anzeigen
Aber tut sie nicht... Ich steig da gerade absolut nicht mehr druch...
Wäre über einen Tip dankbar.
Wer es mal selbst testen möchte, dem habe ich meinen src angehängt
Die beiden Beispiele sind wie folgt zu kompilieren:
g++ -g `xml2-config --cflags --libs` -o _xmlmenu _xmlmenu.cpp # meine erste version mit libxml
gcc -g `pkg-config libxml++-2.6 --cflags --libs` -o xmlmenu xmlmenu.cpp # meine zweite version mit libxml++
Gruß,
Thomas