jeudi 27 mars 2008

Problème de validation XML avec JAXP

L'api JAXP permet l'analyse et la transformation de documents XML. Une implémentation de JAXP est disponible dans JAVASE 5.0 mais il est aussi possible d'utiliser l'implémentation fournie par Apache Xerces.
Pour analyser le fichier data.xml avec un parser SAX :

SAXParserFactory spfactory = SAXParserFactory.newInstance();
spfactory.setNamespaceAware(true);
SAXParser saxparser = spfactory.newSAXParser();
saxparser.parse(new File("data.xml"));

Et avec un parser DOM :

DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
dbfactory.setNamespaceAware(true);
DocumentBuilder domparser = dbfactory.newDocumentBuilder();
domparser.parse(new File("data.xml"));


La fonctionnalité qui nous intéresse ici est la validation d'un document par rapport à un schéma.

DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
dbfactory.setNamespaceAware(true);
dbfactory.setXIncludeAware(true);

DocumentBuilder parser = dbfactory.newDocumentBuilder();
Document doc = parser.parse(new File("data.xml"));

DOMSource xmlsource = new DOMSource(doc);

SchemaFactory wxsfactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

//load a W3C XML Schema
Schema schema = wxsfactory.newSchema(new File("myschema.xsd"));

// create a validator from the loaded schema
Validator validator = schema.newValidator();

//validate the XML instance
validator.validate(xmlsource);

Si cet exemple fonctionne avec l'implémentation fournie avec JAVASE 5.0, une exception est toujours levée avec Xerces :

Exception in thread "main" org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'root'.

En effet, l'implémentation Xerces de Validator#validate ne semble pas supporter un objet de classe DOMSource en paramètre.
Pour contourner ce problème, il faut utiliser la fonctionnalité de transformation de JAXP et transformer notre document DOM(DOMSource) en Stream(StreamSource).

DOMSource xmlsource = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(xmlsource , result);
validator.validate(new StreamSource(new StringReader(result.toString())));


Pour aller plus loin : http://issues.apache.org/jira/browse/XERCESJ-1163