Xerces-C: Problem mit verschachtelten XML-Dateien



  • Moin!
    Ich schreibe derzeit an einem Bibliothekssystem. Man kann Kategorier erstellen und in diese dann mit Medien (Bücher, CDs, etc) füllen. Zudem können Kategorien verschachtelt werden. Eine einfache XML-Datei, die ich generiere sieht so aus (Der Einfachheit halber erstmal ohne Attribute):

    <MediumCategory>
    	<MediumCategory>
    		<MediumCategory>
    			<Medium />
    			<Medium />
    			<Medium />
    		</MediumCategory>
    
    		<Medium />
    		<Medium />
    		<Medium />
    
    	</MediumCategory>
    	<MediumCategory>
    		<Medium />
    		<Medium />
    	</MediumCategory>
    
    	<Medium />
    	<Medium />
    
    </MediumCategory>
    

    Nun will ich so einen abgespeicherten Baum mit Xerces-C einlesen. Ich habe dafür folgenden Algorithmus:

    /** Loads a category tree from the associated file */
    MediumCategory* FileDB::loadCategoryTree()
    {
    	DOMDocument* doc = NULL;
    	DOMNode* node = NULL;
    
    	XMLPlatformUtils::Initialize();
    	XercesDOMParser* parser = new XercesDOMParser();
    	parser->setValidationScheme(XercesDOMParser::Val_Always);
    
    	parser->parse(dbfile.c_str());
    
    	doc = parser->getDocument();
    
    	if(doc == NULL || !doc->hasChildNodes() || (node = doc->getFirstChild()) == NULL)
    	{
    		//There must have been something wrong with the fileformat
    		delete parser;
    		return NULL;
    	}
    
    	//File has the right format... let's load the data
    	MediumCategory* tmp = addXMLCatRecursive(node);
    	delete parser;
    
    	//Mark the data as unchanged
    	tmp->setChangedRecursively(false);
    	return tmp;
    }
    
    MediumCategory* FileDB::addXMLCatRecursive(DOMNode* node, MediumCategory* parentCat)
    {
    	MediumCategory* tmp;
    
    	do
    	{		
    		if(node->getNodeType() == DOMNode::ELEMENT_NODE)
    		{
    			std::string s(XMLString::transcode(node->getNodeName()));
    			DOMNamedNodeMap* nm = node->getAttributes();
    
    			if(nm != NULL)
    			{
    				if(s == "MediumCategory")
    				{
    
    					DOMNode* dn = nm->getNamedItem(XMLString::transcode("name"));
    					if(dn != NULL)
    					{
    						std::string s(XMLString::transcode(dn->getNodeValue()));
    						tmp = new MediumCategory(s, parentCat);
    					}
    
    					if(node->hasChildNodes())
    						addXMLCatRecursive(node->getFirstChild(), tmp);
    				}
    				else if(s == "Medium")
    				{
    					medium_id id;
    					std::string comment;
    					std::string author;
    					std::string serial;
    					std::string title;
    					std::string publisher;
    					std::string isbn;
    					std::string keywords;
    					unsigned int pages = 0;
    					unsigned int year = 0;
    
    					DOMNode* dn = nm->getNamedItem(XMLString::transcode("id"));
    					id = (dn != NULL) ? atoi(XMLString::transcode(dn->getNodeValue())) : 0;
    
    					dn = nm->getNamedItem(XMLString::transcode("author"));
    					author = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					dn = nm->getNamedItem(XMLString::transcode("serial"));
    					serial = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					dn = nm->getNamedItem(XMLString::transcode("title"));
    					title = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					dn = nm->getNamedItem(XMLString::transcode("year"));
    					year = (dn != NULL) ? atoi(XMLString::transcode(dn->getNodeValue())) : 0;
    
    					dn = nm->getNamedItem(XMLString::transcode("pages"));
    					pages = (dn != NULL) ? atoi(XMLString::transcode(dn->getNodeValue())) : 0;
    
    					dn = nm->getNamedItem(XMLString::transcode("comment"));
    					comment = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					dn = nm->getNamedItem(XMLString::transcode("publisher"));
    					publisher = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					dn = nm->getNamedItem(XMLString::transcode("isbn"));
    					isbn = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					dn = nm->getNamedItem(XMLString::transcode("keywords"));
    					keywords = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					Medium* medium = new Medium(id, serial, author, year, title, comment, publisher, isbn, keywords, pages);
    					if(parentCat != NULL)
    						medium->setCategory(parentCat);
    				}
    			}
    		}
    	} while((node = node->getNextSibling()) != NULL);
    	///FIXME: it seems as if the recursion confuses the getNextSibling()-method
    	return tmp;
    }
    

    Ich weiß, da sind ein paar furchtbare Dinge im Code zu sehen 😃 Aber mein Hauptproblem kann ich immer noch nicht lösen. Es werden zwar verschachtelte Kategorien korrekt eingelesen, aber Geschwisterknoten werden nur auf allerunterster Ebene eingelesen. Ich nehme an, dass das mit dem Verhalten von getNextSibling() zusammenhängt. Ich habe schon versucht den Code so umzuschreiben, dass die Liste der zu verarbeitenden Knoten ganz am Anfang erstellt wird. Das hat aber auch nichts gebracht.

    Um mein Problem an dem Beispiel nochmal konkret zu erläutern:
    - Dünne Bereiche werden eingelesen
    - Fett markierte Bereiche werden nicht eingelesen

    <MediumCategory>
    	<MediumCategory>
    		<MediumCategory>
    			<Medium />
    			<Medium />
    			<Medium />
    		</MediumCategory>
    
    		<Medium />
    		<Medium />
    		<Medium />
    
    	</MediumCategory>
    	[b]<MediumCategory>
    		<Medium />
    		<Medium />
    	</MediumCategory>
    
    	<Medium />
    	<Medium />[/b]
    
    </MediumCategory>
    

    Kann mir jemand weiterhelfen?



  • Es ist nur ein Verdacht, aber vielleicht funktioniert getNextSibling() einwandfrei und Du merkst es nicht. Ich benutze auch Xerces und ich habe nicht dieses Problem.

    Zu Deinem Code:
    1.) Der Return-Wert 'tmp' ist nicht definiert, falls kein ELEMENT_NODE gefunden wurde oder falls kein ELEMENT_NODE Attribute hatte.
    2.) Falls der ELEMENT_NODE - und z.B. <MediumCategory> ist einer - selbst keine Attribute hat, so werden in Deiner Schleife auch keine Child-Knoten mehr aufgerufen. Das könnte die Ursache Deines Problems sein.
    3.) Falls eine <MediumCategory> kein Attribut 'name' hat, so ist der Parameter 'parentCat' im nächsten Aufruf von 'addXMLCatRecursive' nicht definiert.

    Tipp: Ersetzt do {} while(); durch

    for( ; node; node = node->getNextSibling() )
        {
            if( node->getNodeType() != DOMNode::ELEMENT_NODE )
                continue;
            // usw...
    

    Gruß
    Werner



  • OK, ich hab's jetzt mal umgeschrieben:

    MediumCategory* FileDB::addXMLCatRecursively(DOMNode* node, MediumCategory* parentCat)
    {
    	MediumCategory* tmp;
    
    	for( ; node; node = node->getNextSibling() ) 
    	{
    
    		if( node->getNodeType() != DOMNode::ELEMENT_NODE )
    			continue; 
    
    			std::string s(XMLString::transcode(node->getNodeName()));
    			DOMNamedNodeMap* nm = node->getAttributes();
    
    			if(nm != NULL)
    			{
    				if(s == "MediumCategory")
    				{
    
    					DOMNode* dn = nm->getNamedItem(XMLString::transcode("name"));
    					if(dn != NULL)
    					{
    						std::string s(XMLString::transcode(dn->getNodeValue()));
    						tmp = new MediumCategory(s, parentCat);
    					}
    				}
    				else if(s == "Medium")
    				{
    					medium_id id;
    					std::string comment;
    					std::string author;
    					std::string serial;
    					std::string title;
    					std::string publisher;
    					std::string isbn;
    					std::string keywords;
    					unsigned int pages = 0;
    					unsigned int year = 0;
    
    					DOMNode* dn = nm->getNamedItem(XMLString::transcode("id"));
    					id = (dn != NULL) ? atoi(XMLString::transcode(dn->getNodeValue())) : 0;
    
    					dn = nm->getNamedItem(XMLString::transcode("author"));
    					author = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					dn = nm->getNamedItem(XMLString::transcode("serial"));
    					serial = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					dn = nm->getNamedItem(XMLString::transcode("title"));
    					title = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					dn = nm->getNamedItem(XMLString::transcode("year"));
    					year = (dn != NULL) ? atoi(XMLString::transcode(dn->getNodeValue())) : 0;
    
    					dn = nm->getNamedItem(XMLString::transcode("pages"));
    					pages = (dn != NULL) ? atoi(XMLString::transcode(dn->getNodeValue())) : 0;
    
    					dn = nm->getNamedItem(XMLString::transcode("comment"));
    					comment = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					dn = nm->getNamedItem(XMLString::transcode("publisher"));
    					publisher = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					dn = nm->getNamedItem(XMLString::transcode("isbn"));
    					isbn = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					dn = nm->getNamedItem(XMLString::transcode("keywords"));
    					keywords = (dn != NULL) ? XMLString::transcode(dn->getNodeValue()) : "";
    
    					Medium* medium = new Medium(id, serial, author, year, title, comment, publisher, isbn, keywords, pages);
    					if(parentCat != NULL)
    						medium->setCategory(parentCat);
    				}
    			}
    
    		if(node->hasChildNodes())
    			addXMLCatRecursively(node->getFirstChild(), tmp);
    
    	}
    	return tmp;
    }
    

    Leider ist überhaupt gar keine Veränderung zu bemerken... Kannst Du mir eventuell mal einen Algorithmus cvon Dir schicken, der so eine geparste XML-Datei rekursiv durchläuft?



  • hackbert schrieb:

    OK, ich hab's jetzt mal umgeschrieben:

    Mmmmh .. nicht wirklich. Du hast zwar meinen Tipp befolgt - aber die Änderung bewirkt keine funktionale Änderung. Man sieht jetzt halt besser, wie die Schleife kontrolliert wird und wo getNextSibling() überhaupt aufgerufen wird - beim ersten Mal habe ich ziemlich gesucht 😉

    Beachte bitte Punkt 2 und 3 meines obigen Postings.

    hackbert schrieb:

    std::string s(XMLString::transcode(node->getNodeName()));
    			DOMNamedNodeMap* nm = node->getAttributes();
    			
    			if(nm != NULL)  // <--
    			{
                                // usw.
    			}
    

    <--- hier wird alles übersprungen, falls keine Attribute vorhanden sind! Hast Du das überprüft? Poste doch einfach mal die Original XML-Datei - dann muss ich hier nicht dauernd die Glaskugel bemühen 😃 . Dein Code ist nun mal so geschrieben, dass er nicht in jedem Fall alle XML-Elemente rekursiv durchlaufen werden.

    hackbert schrieb:

    Kannst Du mir eventuell mal einen Algorithmus cvon Dir schicken, der so eine geparste XML-Datei rekursiv durchläuft?

    Darf ich leider nicht - weil der Code nutzt eine selbstgebaute Library, die als C++-Wrapper auf Xerces arbeitet und auf der ist 'n Copyright drauf 🙄

    Gruß
    Werner



  • Das Ergebnis ist doch für das erste Kind richtig, nur die folgenden Kinder der ersten Ebene werden nicht bearbeitet. Deswegen könnte die rekusive Fkt. stimmen, dafür aber der initiale Knoten falsch sein???

    // probier mal:
    (node = doc->getDocumentElement()) 
    // statt:
    (node = doc->getFirstChild())
    

    :xmas1:, don_basto



  • Bringt leieder auch nichts... Ich habe glaube ich vergessen zu erwähnen, dass es ein Wurzelement des Typs MediumCategory gibt. Alle anderen Kategorien stecken da drin.

    Hier ist die Datei, die ich aktuell einlesen will: http://drebesium.ath.cx/~hackbert/wxbiblio/testdata/buchkatalog.xml

    Da ist echt der Wurm drinne. Ich suche nun schon so einige Stunden nach dem Fehler...



  • Die XML-Datei ist nicht wohlgeformt meint Mozilla Firefox.



  • Oh mein Gott! Das war es. Habe ein & nicht als &-Entity geschrieben. Jetzt geht es. Dankeschön trotzdem...



  • ROFLMAO 😃 👍


Anmelden zum Antworten