2009-07-07 17 views
32

Sto cercando di convalidare un file XML con una serie di schemi diversi (scuse per l'esempio inventato):convalidare un file XML con più definizioni di schema

  • A.xsd
  • B.xsd
  • c.xsd

c.xsd in particolare alle importazioni B.xsd e importazioni B.xsd A.xsd, utilizzando:

<xs:include schemaLocation="b.xsd"/>

che sto cercando di fare questo via Xerces nel modo seguente:

XMLSchemaFactory xmlSchemaFactory = new XMLSchemaFactory(); 
Schema schema = xmlSchemaFactory.newSchema(new StreamSource[] { new StreamSource(this.getClass().getResourceAsStream("a.xsd"), "a.xsd"), 
                 new StreamSource(this.getClass().getResourceAsStream("b.xsd"), "b.xsd"), 
                 new StreamSource(this.getClass().getResourceAsStream("c.xsd"), "c.xsd")});  
Validator validator = schema.newValidator(); 
validator.validate(new StreamSource(new StringReader(xmlContent))); 

ma questo non riesce a importare tutti e tre gli schemi risultanti correttamente non può risolvere il nome di 'bla' a un (n) componente "gruppo".

ho convalidato questo successo utilizzando Python, ma avere problemi reali con Java 6.0 e Xerces 2.8.1. Qualcuno può suggerire cosa sta andando storto qui, o un approccio più semplice per convalidare i miei documenti XML?

risposta

17

Quindi, nel caso in cui qualcun altro si trovi di fronte allo stesso problema, ho dovuto caricare uno schema padre (e schemi figlio impliciti) da un test unitario - come risorsa - per convalidare una stringa XML. Ho usato Xerces XMLSchemFactory per farlo insieme al validatore Java 6.

Al fine di caricare il bambino lo schema di corretta tramite un include ho dovuto scrivere un resolver risorsa personalizzata. Il codice può essere trovato qui:

https://code.google.com/p/xmlsanity/source/browse/src/com/arc90/xmlsanity/validation/ResourceResolver.java

Per utilizzare il resolver specificarlo sulla fabbrica schema:

xmlSchemaFactory.setResourceResolver(new ResourceResolver()); 

e lo userà per risolvere le vostre risorse attraverso il classpath (nel mio caso da src/main/risorse). Qualsiasi commento è benvenuto su questo ...

+3

Qualche possibilità di approfondire ulteriormente la questione su come il resolver delle risorse personalizzate fa funzionare tutto questo? Grazie. – Casey

+0

Posso aggiungere che devi aggiungere qualcosa come: ' 'nel genitore xsd caricato con' nuovo StreamSource (this.getClass(). GetResourceAsStream ("parent.xsd") ' –

+1

Hai creato uno schema padre" artificiale "che ha importato tutti gli altri? – zedoo

2

La roba schema in Xerces è (a) molto, molto pedante, e (b) dà messaggi di errore del tutto inutile quando non gli piace ciò che trova. È una combinazione frustrante.

La roba schema in pitone può essere molto più indulgente, ed è stato lasciare che piccoli errori nello schema andare oltre non dichiarata.

Ora se, come dici tu, c.xsd include b.xsd e b.xsd include a.xsd, quindi non è necessario caricarli tutti e tre nello schema factory. Non solo non è necessario, ma probabilmente confonderà Xerces e causerà errori, quindi questo potrebbe essere il tuo problema. Basta passare c.xsd alla fabbrica e lasciare che risolva b.xsd e a.xsd stesso, cosa che dovrebbe fare relativamente a c.xsd.

+0

Già questo sembra provocare lo stesso errore anche. Mi chiedo se le dichiarazioni di importazione nei file dello schema stanno causando problemi ... Non aiuta che due degli schemi non abbiano uno spazio dei nomi di destinazione sia ... gargh – Jon

+0

Forse uno dei modi per risolvere questo è usare un ResourceResolevr e impostalo sullo schema factory ... – Jon

+4

Sei sicuro di non confondere import e includere? Significano due cose diverse e non dovrebbero essere confuse. Are, Be, in diversi namespace? Se è così, allora dovrebbero essere importati, non inclusi. Se sono nello stesso spazio dei nomi, dovrebbero essere inclusi. – skaffman

6

http://www.kdgregory.com/index.php?page=xml.parsing sezione 'schemi multipli per un singolo documento'

La mia soluzione sulla base di tale documento:

URL xsdUrlA = this.getClass().getResource("a.xsd"); 
URL xsdUrlB = this.getClass().getResource("b.xsd"); 
URL xsdUrlC = this.getClass().getResource("c.xsd"); 

SchemaFactory schemaFactory = schemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 
//--- 
String W3C_XSD_TOP_ELEMENT = 
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" 
    + "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" elementFormDefault=\"qualified\">\n" 
    + "<xs:include schemaLocation=\"" +xsdUrlA.getPath() +"\"/>\n" 
    + "<xs:include schemaLocation=\"" +xsdUrlB.getPath() +"\"/>\n" 
    + "<xs:include schemaLocation=\"" +xsdUrlC.getPath() +"\"/>\n" 
    +"</xs:schema>"; 
Schema schema = schemaFactory.newSchema(new StreamSource(new StringReader(W3C_XSD_TOP_ELEMENT), "xsdTop")); 
+0

ha funzionato per me. Ricordati di utilizzare l'importazione nel caso in cui lo schema incluso abbia uno spazio dei nomi di destinazione diverso –

3

Dalla documentazione Xerces: http://xerces.apache.org/xerces2-j/faq-xs.html

import javax.xml.transform.Source; 
import javax.xml.transform.stream.StreamSource; 
import javax.xml.validation.Schema; 
import javax.xml.validation.SchemaFactory; 
import javax.xml.validation.Validator; 

... 

StreamSource[] schemaDocuments = /* created by your application */; 
Source instanceDocument = /* created by your application */; 

SchemaFactory sf = SchemaFactory.newInstance(
    "http://www.w3.org/XML/XMLSchema/v1.1"); 
Schema s = sf.newSchema(schemaDocuments); 
Validator v = s.newValidator(); 
v.validate(instanceDocument); 
+0

La tua risposta riguarda la convalida di più schemi? Potrebbe essere necessario aggiungere un ciclo per raffigurarlo. –

+1

Vale la pena ricordare che l'ordine dello schema è importante qui. Se A importa B, allora devi avere B prima di A nell'array che usi per creare lo schema. – Hedley

+0

questa risposta è per un semplice XSD, non per molti – Weslor

1

Ho finito per usare questo:

import org.apache.xerces.parsers.SAXParser; 
import org.xml.sax.SAXException; 
import org.xml.sax.SAXParseException; 
import org.xml.sax.helpers.DefaultHandler; 
import java.io.IOException; 
. 
. 
. 
try { 
     SAXParser parser = new SAXParser(); 
     parser.setFeature("http://xml.org/sax/features/validation", true); 
     parser.setFeature("http://apache.org/xml/features/validation/schema", true); 
     parser.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true); 
     parser.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", "http://your_url_schema_location"); 

     Validator handler = new Validator(); 
     parser.setErrorHandler(handler); 
     parser.parse("file:///" + "/home/user/myfile.xml"); 

} catch (SAXException e) { 
    e.printStackTrace(); 
} catch (IOException ex) { 
    e.printStackTrace(); 
} 


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 { 
    } 
} 

ricordarsi di cambiare:

1) Il parametro "http: // your_url_schema_location" per voi XSD posizione del file.

2) La stringa "/home/user/myfile.xml" per quella che punta al file xml.

non ho dovuto impostare la variabile: -Djavax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema=org.apache.xerces.jaxp.validation.XMLSchemaFactory

0

Ho affrontato lo stesso problema e dopo avere indagato trovato questa soluzione. Per me funziona.

Enum l'impostazione di vari XSDs:

public enum XsdFile { 
    // @formatter:off 
    A("a.xsd"), 
    B("b.xsd"), 
    C("c.xsd"); 
    // @formatter:on 

    private final String value; 

    private XsdFile(String value) { 
     this.value = value; 
    } 

    public String getValue() { 
     return this.value; 
    } 
} 

Metodo per la convalida:

public static void validateXmlAgainstManyXsds() { 
    final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 

    String xmlFile; 
    xmlFile = "example.xml"; 

    // Use of Enum class in order to get the different XSDs 
    Source[] sources = new Source[XsdFile.class.getEnumConstants().length]; 
    for (XsdFile xsdFile : XsdFile.class.getEnumConstants()) { 
     sources[xsdFile.ordinal()] = new StreamSource(xsdFile.getValue()); 
    } 

    try { 
     final Schema schema = schemaFactory.newSchema(sources); 
     final Validator validator = schema.newValidator(); 
     System.out.println("Validating " + xmlFile + " against XSDs " + Arrays.toString(sources)); 
     validator.validate(new StreamSource(new File(xmlFile))); 
    } catch (Exception exception) { 
     System.out.println("ERROR: Unable to validate " + xmlFile + " against XSDs " + Arrays.toString(sources) 
       + " - " + exception); 
    } 
    System.out.println("Validation process completed."); 
} 
Problemi correlati