validazione xml con schema: messaggio di errore adeguato

293 views
Skip to first unread message

Enrico Giurin

unread,
Mar 25, 2009, 7:52:08 AM3/25/09
to jugp...@googlegroups.com
Ciao lista,
ho il seguente problema.
Devo validare un xml con un xml-schema e fino a qua non c'e' nessun
problema, utilizzo l'implementazione (apache ?) di
javax.xml.validation.Validator.
Il problema e' che non riesco a risalire, dal messaggio di errore,
all' attributo che non soddisfa le regole definite nel mio schema,
avendo un messaggio di errore di questo tipo:

org.xml.sax.SAXParseException: cvc-pattern-valid: Value '@pippo' is
not facet-valid with respect to pattern '[A-Za-z0-9._]*' for type
'null'.
at com.sun.org.apache.xerces.internal.jaxp.validation.Util.toSAXParseException(Util.java:109)

Cioe' vorrei avere a disposizione sull'eccezione generata, un metodo
del tipo List<String> getErrors() che mi ritorni la lista degli
attributi che non soddisfano lo schema.
Questo perché devo fornire ad una GUI un conveniente messaggio di
errore, e l'informazione di quale attributo non e' valido.

La domanda e', come risolvereste il problema, oppure dovrei usare un'
altra api per la validazione?

Grazie dell' attenzione,
Enrico.

Federico Cattozzi

unread,
Mar 26, 2009, 6:10:02 AM3/26/09
to JUG Padova
Dovresti secondo me implementare in modo opportuno l'handler del
validatore, se ce la fai posta il risultato.
Sarebbe interessante individuare con xPath il punto preciso che genera
l'errore di validazione (nel caso di xml well-formed ovviamente).
Ti do un hint postandoti il codice che uso io con un handler
personalizzato, per validare uso JAXP.

Gli import sono:

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

Il codice che valida è:

// VALIDA L'XML USANDO JAXP
String rqXSDPath = "/mio_schema.xsd";
org.w3c.dom.Document docW3C = null;
try{
System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
"org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(true);
factory.setAttribute("http://java.sun.com/xml/jaxp/properties/
schemaLanguage",
"http://www.w3.org/2001/XMLSchema" );
factory.setAttribute("http://java.sun.com/xml/jaxp/properties/
schemaSource", rqXSDPath);
DocumentBuilder builder = factory.newDocumentBuilder();
Validator handler = new Validator();
builder.setErrorHandler(handler);
docW3C = builder.parse(inBuffered);
if(handler.validationError == true) {
handler.saxParseException.getMessage();
}
} catch(java.io.IOException ioe) {
ioe.printStackTrace();
}
catch (SAXException e) {
e.printStackTrace();
}
catch (ParserConfigurationException e) {
e.printStackTrace();
} }

La classe nested Validator personalizzata richiamata nel codice sopra
è:

private class Validator extends DefaultHandler {
public boolean validationError =false;
public SAXParseException saxParseException=null;
public void error(SAXParseException exception) throws SAXException {
validationError = true;
saxParseException=exception;
}

public void fatalError(SAXParseException exception) throws
SAXException {
validationError = true;
saxParseException=exception;
}

public void warning(SAXParseException exception) throws SAXException
{}}

Enrico Giurin

unread,
Mar 26, 2009, 7:41:20 AM3/26/09
to jugp...@googlegroups.com
Ciao Federico,
grazie per la tua mail.
Il codice per validare xml con xml-schema lo avevo gia' implementato
ed e' grosso modo equivalente a quanto tu hai scritto.
Come tu hai scritto, se implementi un custom DefaultHandler hai a
disposizione tre metodi ma nessuno dei tre ti permette di risalire
all' attributo che fallisce la validazione, certo riesci a capire il
numero di riga e di colonna dove avviene l'errore ma non e' proprio
quello che cercavo.

<<Debuggando>> un po' il mio codice ho visto che ad un certo punto, in
corrispondenza dell' eccezione generata, viene chiamato il metodo
reportError() della classe
com.sun.org.apache.xerces.internal.impl.XMLErrorReporter , da cui e'
possibile risalire al valore dell' attributo che ha generato l'errore.
Avrebbe quindi senso estendere questa classe, fornire un conveniente
metodo che stampa il valore dell' attributo che fallisce la
validazione e in qualche modo <<iniettare>> questa classe nel
meccanismo di validazione xml.
Pero' mi sono fermato qua, perche' ho trovato un metodo spiccio per
risolvere il mio problema, ovvero, per ciascuno attributo che e'
cambiato eseguo la validazione xml su un clone (ottenuto da
cloneNode(boolean deep) ) del mio Document. Quindi in questo modo
prima di iniettarlo nel mio vero Documento sono sicuro che il valore
dell' elemento/attributo e' valido. Soluzione non elegante ma funziona
:)



2009/3/26 Federico Cattozzi <federico...@gmail.com>:

Federico Cattozzi

unread,
Mar 27, 2009, 5:30:03 AM3/27/09
to JUG Padova
Ah, ma sei tu che crei l'xml che poi lo validi, o ho capito male?
Nel mio caso ho un'applicazione web che riceve un xml via http da
un'altra applicazione in internet e devo validarlo prima di inserire i
dati che contiene in un database.
In quel caso non riesco ad isolare il nodo che ha prodotto l'eccezione
nel SAXParser e l'unico messaggio che posso dare all'utente è il
contenuto dell'eccezione.
Mi par strano che non sia possibile isolare il nodo, probabilmente
come hai detto tu bisogna scendere ad un livello più basso, quello del
parser SAX, e ri-implementare/estendere certe classi.

Per scorrere invece l'albero del Document una volta che è validato,
uso il framework JDOM, molto meglio del DOM del W3C che trovi nel JDK,
perché ha metodi molto più "javeschi" per interpretare i nodi
dell'albero e ha delle classi molto comode per stampare l'xml con vari
formatter.
Usando JDOM mi sono scritto due comodi metodi statici per localizzare
con una espressione xPath eventuali nodi che a livello di "business
logic" non sono corretti (nel caso di dati già inviati, quindi doppi o
altro).
Se ti possono servire eccoli:

import org.jdom.Attribute;
import org.jdom.Element;

public static String getXPath(Attribute attribute) {
return getXPath(attribute.getParent()) + "/@" + attribute.getName();

}

public static String getXPath(Element element) {
String xPath = "/" + element.getName();
if (element.getParentElement() != null) {
List brothers = element.getParentElement().getChildren();
Iterator i = brothers.iterator();
int index = 0;
while (i.hasNext()) {
index++;
if (((Element)i.next()) == element) {
xPath += "[" + index + "]";
return getXPath(element.getParentElement()) + xPath;
}
}
}
return xPath;
}

Ciao e buon lavoro!
> 2009/3/26 Federico Cattozzi <federico.catto...@gmail.com>:
Reply all
Reply to author
Forward
0 new messages