La vérification de fichiers XML en fonction de schémas XSD peut vite devenir plus complexe qu'elle n'en a l'air...
Imaginons une application Java qui utilise des schémas imbriqués situés dans un certain répertoire pour valider des fichiers situer dans un autre répertoire :
Dans C:\schemas\
:
schema-parent.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0">
<xs:include schemaLocation="schema-fils.xsd"/>
<xs:element name="OPS">
<xs:annotation>
<xs:documentation>bla bla.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:choice>
...
</xs:schema>
schema-fils.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0">
<xs:include schemaLocation="schema-petit-fils.xsd"/>
<xs:element name="TOT">
<xs:annotation>
<xs:documentation>bla bla.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:choice>
...
</xs:schema>
schema-petit-fils.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0">
<xs:element name="XDF">
<xs:annotation>
<xs:documentation>bla bla.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:choice>
...
</xs:schema>
Dans C:\xml\
:
avalider.xml
<?xml version="1.0" encoding="UTF-8"?>
<OPS xmlns="..." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="schema-parent.xsd" AD="FRA" FR="AA"
ON="540" OD="2012-11-12" OT="15:08">
<DAT TM="CU">
...
</DAT>
</OPS>
Et ci-dessous le code permettant d'exécuter la validation :
...
import org.xml.sax.helpers.DefaultHandler;
...
XSDHandler handler = new XSDHandler();
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
spf.setValidating(true);
SAXParser sp = spf.newSAXParser();
sp.getParser().setLocale(Locale.ENGLISH);
sp.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
InputStream schemaIs = getClass().getResourceAsStream("schema-parent.xsd");
sp.setProperty("http://java.sun.com/xml/jaxp/properties/schemaSource", schemaIs);
sp.parse(fichierAvalider, handler);
...
class XSDHandler extends DefaultHandler {
private final static Logger logger = Logger.getLogger(XSDHandler.class);
@Override
public void fatalError(SAXParseException e) {
logger.error("Erreur fatale :" + message);
}
@Override
public void error(SAXParseException e) {
logger.error("Erreur :" + message);
}
@Override
public void warning(SAXParseException e) {
logger.warn("Warning :" + message);
}
}
Lors de l'exécution, l'erreur suivante va être levée :
Echec de la lecture du document de schéma 'schema-fils.xsd' pour les raisons suivantes : 1) Le document est introuvable ; 2) Le document n'a pas pu être lu ; 3) L'élément racine du document n'est pas .
Ceci est du à la présence de l'attribut schemaLocation
au sein même du fichier XML, qui prévaut sur toute autre déclaration schemaLocation
dans les XSD. Le SAXParser va donc tenter de trouver le schéma relativement au fichier en cours de vérification (dans C:\xml\
)... Il est donc préférable de supprimer cet attribut.
Cependant, même après cette correction, l'erreur persiste... En effet, dans ce cas de schémas imbriqués, le SAXParser utilise l'attribut schemaLocation
de schema-parent.xsd
contenant normalement le chemin relatif vers le schéma fils, mais recherche toujours de manière relative au fichier XML!
Pour régler ce problème, sans avoir à indiquer de chemin absolu dans les schémas, il est possible d'indiquer au SAXParser comment retrouver ses petits. Pour ce faire, il faut modifier le XSDHandler
qui lui est passé pour surcharger la méthode resolveEntity
:
class XSDHandler extends DefaultHandler {
private final static Logger logger = Logger.getLogger(XSDHandler.class);
@Override
public void fatalError(SAXParseException e) {
logger.error("Erreur fatale :" + message);
}
@Override
public void error(SAXParseException e) {
logger.error("Erreur :" + message);
}
@Override
public void warning(SAXParseException e) {
logger.warn("Warning :" + message);
}
@Override
public InputSource resolveEntity(String arg0, String arg1)
throws IOException, SAXException {
File tmp = new File(arg1);
return new InputSource(new FileInputStream(new File(getClass().getResource("/v3/"+tmp.getName()).toURI())));
}
}
Le deuxième argument arg1
contient le chemin complet où le parser cherche le schéma imbriqué. Il suffit donc d'un petit tour de passe-passe pour renvoyer le bon fichier!
Hope this helps!
0 commentaires:
Enregistrer un commentaire