2009
11.22

StAX pour Streaming Api for Xml est une méthode performante pour parser un document XML. Voici un petit article qui montre les bien faits de cette méthode permettant de parcourir un fichier XML. On connaît bien les méthodes DOM (Document Object Model) et SAX (Simple Api for Xml), mais un peu moins sur le StAX méthode dite de pull parsing.

Logo XML 1.1

Logo XML 1.1

Pour rappel, la méthode DOM a pour fonctionnement le chargement en mémoire de l’ensemble d’un fichier XML sous forme d’arbre ou tableau etc… Cette méthode convient pour des fichiers de petite taille mais pour des fichiers de plusieurs centaines de mégaoctets voir quelques gigaoctets, ce mode est très lent. DOM a la particularité de pouvoir parcourir un fichier dans un sens comme dans l’autre et de permettre l’écriture et la modification de nœuds XML. Le mode de parcours SAX est un mode dit de push parsing (évènement implémenté/déclenché du côté de l’API) qui permet le parcours d’un fichier uniquement vers l’avant. SAX consomme très peu de mémoire, et reste très rapide mais ne permet pas la modification de nœuds XML.

La notion de pull parsing pour le StAX vient du fait que l’évènement est demandée du côté du code client (comprendre le code client en dehors de l’API XML). Le parseur est ce qu’on appelle « stream based », le code client soumet la portion de code a analyser par le parseur et récupère entre autre un évènement au quel on choisira de réaliser une action.
Le pull parsing oblige une analyse vers l’avant uniquement et permet l’écriture mais pas la modification de nœuds XML. Les performance sont très élevées pour le parcours de fichiers volumineux.

Parseur de type StAX :

  • Expat (C)
  • SPXML (C++)
  • Plusieurs implémentations en JAVA basées sur la JSR-173 (JAVA)

Ci-dessous un exemple d’utilisation du parseur SPXML en mode StAX (supporte aussi DOM). On peut voir que le code utilisateur implémente les actions de traitement et non une redéfinition de méthodes de l’API comme en SAX. Notons que j’ai choisit de parcourir le fichier donné en paramètre, ligne à ligne, mais StAX étant stream based (basé sur des flots de données), il possible de parcourir selon un nombre d’octet définit.

int main( int argc, const char * argv[] )
{
	std::string filename;
	if( argc != 2 )
	{
		std::cout << "Usage:" << argv[0] << " <xml_file>" << std::endl;
		return (-1);
	}
	else
		filename = argv[1];

	SP_XmlPullParser parser;
	std::string line;
	std::ifstream myfile(filename.c_str());
	if (myfile.is_open())
	{
		while (!myfile.eof())
		{
			std::getline(myfile, line);
			parser.append( line.c_str(), line.length() );
			for( SP_XmlPullEvent * event = parser.getNext(); NULL != event; event = parser.getNext() )
			{
				switch( event->getEventType() )
				{
					case SP_XmlPullEvent::eStartDocument:
						std::cout << "start document" << std::endl;
						break;
					case SP_XmlPullEvent::eEndDocument:
						std::cout << "\nend document" << std::endl;
						break;
					case SP_XmlPullEvent::eDocDecl:
					{
						SP_XmlDocDeclEvent * declEvent = (SP_XmlDocDeclEvent*)event;
						std::cout << "<?xml";
						if( '\0' != *declEvent->getVersion() )
							std::cout << " version=\"" << declEvent->getVersion() << "\"";

						if( '\0' != *declEvent->getEncoding() )
							std::cout << " encoding=\"" << declEvent->getEncoding() << "\"";

						if( -1 != declEvent->getStandalone() )
							std::cout << " standalone=\""<< (declEvent->getStandalone() ? "yes" : "no") << "\"";

						std::cout << "?>" << std::endl;
						break;
					}
					case SP_XmlPullEvent::eDocType:
					{
						SP_XmlDocTypeEvent * typeEvent = (SP_XmlDocTypeEvent*)event;
						std::cout << "<!DOCTYPE " << typeEvent->getName() << " PUBLIC \"" << typeEvent->getPublicID() << "\" SYSTEM \"" << typeEvent->getSystemID() << "\" \"" << typeEvent->getDTD() << "\">" << std::endl;
						break;
					}
					case SP_XmlPullEvent::eStartTag:
					{
						SP_XmlStartTagEvent * stagEvent = (SP_XmlStartTagEvent*)event;
						std::cout << "<" << stagEvent->getName();
						for( int i = 0; i < stagEvent->getAttrCount(); i++ )
						{
							const char * name = NULL, * value = NULL;
							name = stagEvent->getAttr( i, &value );
							std::cout << " " << name << "=\"" << value << "\"";
						}
						std::cout << ">" << std::endl;
						break;
					}
					case SP_XmlPullEvent::eEndTag:
						std::cout << "</" << ((SP_XmlTextEvent*)event)->getText() << ">" << std::endl;
						break;
					case SP_XmlPullEvent::eCData:
					{
						SP_XmlStringBuffer buffer;
						SP_XmlStringCodec::encode( parser.getEncoding(),
						((SP_XmlTextEvent*)event)->getText(), &buffer );
						std::cout << buffer.getBuffer() << std::endl;
						break;
					}
					case SP_XmlPullEvent::eComment:
						std::cout << "<!--" << ((SP_XmlTextEvent*)event)->getText() << "-->" << std::endl;
						break;
					case SP_XmlPIEvent::ePI:
						std::cout << "<?" << ((SP_XmlPIEvent*)event)->getTarget() << " " << ((SP_XmlPIEvent*)event)->getData() << "?>" << std::endl;
						break;
				};
				delete event;
			}
		}
		myfile.close();
		if( NULL != parser.getError() )
			std::cerr << "\nerror: " << std::string(parser.getError()) << std::endl;
	}
	else
		std::cerr << "\nCan't open " << filename << " !!!" << std::endl;

	return 0;
}

Aucun commentaire.

Ajoutez votre commentaire