11544 sujets

JavaScript, DOM et API Web HTML5

Bonjour,

Je cherche à tester si un fichier XML est bien-formé, en javascript, et si possible pouvoir localiser l'erreur le cas échéant. Je pensais pouvoir m'en tirer avec les expressions régulières, mais je n'en suis plus aussi sûr, et mes recherches laborieuses sur le net pour trouver un algo propre se sont révélés infructueuses.

Auriez vous des tuyaux ?

Merci d'avance...
Finalement, j'ai développé ma propre fonction, en ne testant que la validité des balises en ouverture/fermeture ;

voilà le code pour ceux que ça intéresse. N'hésitez pas à faire part de vos améliorations, notamment en ce qui concerne la performance (beaucoup d'expression régulière dans ce code)


function checkXML( xml ) {
	regExp = /(<(\/?)(?:\w+:)?([^>^ ]*)(?: [^>]*)?(\/?)>)/gi;
	
	openingTag = function( tag ) {
		tag = tag.replace( / /gi, "" );
		return tag.match( new RegExp("< *[^/>]+ *>","gi") );
	}
	closingTag = function( tag ) {
		tag = tag.replace( / /gi, "" );
		return tag.match( new RegExp("< */[^/>]+ *>","gi") );
	}
	matchingTags = function( openingTag, closingTag ) {
		var expression = new RegExp("< */ *([^/> ]+) *>","gi");
		var tags = expression.exec( closingTag );
		return openingTag.match( new RegExp("< *"+tags[1]+"( +[^>]*)? *>","gi") );
	}

	// récupération des balises
	var tags = xml.match( regExp );

	var stack = new Array();
	for (var i = 0; i < tags.length; i++) {
		if ( openingTag(tags[ i ]) ) {
			// on met la balise ouvrante sur la pile
			stack.push(tags[ i ]);
		} else if ( closingTag(tags[ i ]) ) { // test superflu
			// on verifie que le sommet de la pile contient la balise ouvrante correspondante
			if ( stack.length==0  )
				return false;
			var tag = stack.pop();
			if ( !matchingTags( tag, tags[ i ] ) ) 
				return false;
		}
	}
   
	// la pile doit être vide
	if ( stack.length!==0 )
		return false;
	else return true;
}

Modifié par lex (03 Apr 2008 - 08:55)
Salut,

La validation d'un document XML est quelque chose de potentiellement très compliqué, qui peut difficilement être simulé avec quelques expression régulières. Par exemple, ta fonction ne teste pas le cas où l'on a des commentaires dans le document (ce n'est qu'un exemple parmi les plus simples).

Je pense que l'idéal est d'utiliser les parseurs XML intégrés au navigateur (l'ActiveX Microsoft.XMLDOM pour IE et document.implementation.createDocument pour les autres), mais ça demande un peu de boulot de recherche.
En effet, comme je le disais je me contente de tester l'ouverture/fermeture des balises ; la "bonne formation" d'un document xml doit obéir aux règles suivantes :
- une seule balise racine
- toute balise ouverte doit être refermée
- les noms des balises doivent commencer par une lettre ou "_", les autres caractères peuvent être des chiffres, des lettres, "_", "." ou "-".
- les noms des balises ne doivent pas commencer par xml
Il me resterait idéalement 3 points à vérifier (en plus de peaufiner le présent algo, qui par exemple ne prend pas en compte les balises ouvrantes-fermantes), ce qui peut être implémenter sans trop de difficulté ; pour l'instant c'est surtout la structuration du fichier qui m'intéresse.

Je ne souhaite pas utiliser les parser intégrés pour la simple raison, que je n'ai aucune maitrise des erreurs retournés - je pense que la plupart du temps je saurais que le document est mal formé, mais rien de plus, or j'ai besoin de signaler l'emplacement où l'erreur a été détecté...
lex a écrit :
Il me resterait idéalement 3 points à vérifier (en plus de peaufiner le présent algo, qui par exemple ne prend pas en compte les balises ouvrantes-fermantes), ce qui peut être implémenter sans trop de difficulté ; pour l'instant c'est surtout la structuration du fichier qui m'intéresse.

C'est une façon de voir les choses, mais je t'assure que la tâche est bien plus compliquée que tu ne l'imagines. Smiley cligne

XML est un méta-langage complexe, et deux ou trois expressions régulières ne suffiront pas à tester efficacement la validité d'un fichier. Voir notamment le doctype, les différentes entités, les espaces de nommage, les commentaires, ...

Après, tout dépend du contexte exact de ton besoin. Si tu connais la source des documents que tu analyses, une solution simple peut être envisagée, mais tu vas y perdre en interopérabilité.
lex a écrit :
Je ne souhaite pas utiliser les parser intégrés pour la simple raison, que je n'ai aucune maitrise des erreurs retournés - je pense que la plupart du temps je saurais que le document est mal formé, mais rien de plus

En es-tu sûr ?
Modifié par Julien Royer (04 Apr 2008 - 14:50)
si l'on utilise IE (6 et au dessus) on peut tout simplement faire (avec pXML étant l'id de l'objet XML) :

var s=" ";
var r="";
var error;
try {
error = pXML.parseError;
if (error.errorCode==0)
return true; // Le flux est valide tout baigne
}
catch (e) {
r="L'élément testé n'est pas un élément XML!";
}
if (r=="") {
for (var i=1; i<error.linepos; i++)
s+=" ";
r += "Le fichier '"+error.url+"' n'est pas valide.\nRaison: "+error.reason;
if (error.line>0)
r+="\nErreur détectée à la ligne "+error.line+", position "+error.linepos+"\n"+error.srcText+"\n"+s+"^";
}
alert(r);
return false; // erreur dans le flux


L'objet pXML peut être créé via le DOM ou récupéré dans un fichier XML soit dynamiquement (via le DOM pXML.load(url)), soit statiquement dans la page HTML:

<XML id="pXML" src="./ficXML/monFlux.xml"></XML>
Je suis aussi a la recherche d'une API javascript pour valider du XML. La validation implementee dans les navigateurs ne me suffit pas, je voudrais valider des champs selon un schema RelaxNG.

Je pense qu'il faut effectivement commencer par une API a la SAX comme tu l'as commence. Est-ce que tu as plus avance sur le sujet ?
je retombe par hasard sur ce vieux sujet...

j'avais complètement oublié que j'avais posté cette question ici, et j'étais très surpris de découvrir que quelqu'un avait adopté la même solution que moi avant de réaliser que c'était mon bout de code Smiley smile

Voilà la version aboutie du code, qui fait à peu près ce que j'attendais de lui, à savoir une simple vérification qu'un fichier XML est bien formé (selon la définition stricte : une seule racine, une bonne correspondance de balises à balises), dans le cadre très limité d'une saisie dans un formulaire d'un fichier XML... Toute amélioration sera la bienvenue.

Si par ailleurs quelqu'un a développé des outils permettant l'édition à la volée de XML en javascript, je suis interessé ; j'ai développé un wrapper de textarea permettant de "controler" la saisie (indentation, vérification, menu de balises, ...), mais j'aimerais bien trouver d'autres approches à ce problème.


function checkXML( xml ) {
	regExp = /(<(\/?)(?:\w+:)?([^>^ ]*)(?: [^>]*[^\/])?( *\/?)>)/gi;
	
	// dans le cas des balises ouvrante
	openingTag = function( tag ) {
		return tag.match( new RegExp("< *(?!/)[^>]*[^/] *>","gi") )?true:false;
	}
	// dans le cas des balises strictement fermante
	closingTag = function( tag ) {
		return tag.match( new RegExp("< */[^>]+>","gi") )?true:false;
	}
	// vérifie que deux balises ouvrantes et fermantes strictes correspondent
	matchingTags = function( openingTag, closingTag ) {
		var expression = new RegExp("< */ *([^>]+) *>","gi");
		var tags = expression.exec( closingTag );
		return openingTag.match( new RegExp("< *"+tags[1]+"( +[^>]*)? *>","gi") );
	}

	// on supprime l'en-tête - A FAIRE : ne devrait pas être reconnu par regExp
	xml = xml.replace( /<\?xml [^<>]*?>/i , "" );

	var stack = new Array();
	// récupération des balises
	var tags = xml.match( regExp );
	/* null si pas de résultats */
	if ( tags!==null ) {
		for (var i = 0; i < tags.length; i++) {
			if ( openingTag(tags[i]) ) {
				// on met la balise ouvrante sur la pile
				stack.push(tags[i]);
			} else if ( closingTag(tags[i]) ) {
				// on verifie que le sommet de la pile contient la balise ouvrante correspondante
				if ( stack.length==0  ) {
					alert( tags[i]+' not open' );
					return false;
				}
				var tag = stack.pop();
				if ( !matchingTags( tag, tags[i] ) ) {
					alert(tag+' and '+tags[i]+' mismatch');
					return false;
				}
			}
		}
	}
	// s'il reste quelque chose sur la pile, alors il manque des balises (si c'est une balise ouvrante) ou qu'il y en a trop (balise fermante)
	if ( stack.length!==0 ) {
		while ( tag = stack.pop() )
			alert( tag+' has no match' );
		return false;
	}
	else return true;
}
[/i][/i][/i][/i][/i][/i]
Modifié par lex (21 Dec 2008 - 11:20)