web-dev-qa-db-fra.com

Valider un fichier XML par rapport à plusieurs définitions de schéma

J'essaie de valider un fichier XML par rapport à un certain nombre de schémas différents (excuses pour l'exemple artificiel):

  • a.xsd
  • b.xsd
  • c.xsd

c.xsd en particulier importe b.xsd et b.xsd importe a.xsd, en utilisant:

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

J'essaye de le faire via Xerces de la manière suivante:

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)));

mais cela ne parvient pas à importer correctement les trois schémas, ce qui ne permet pas de résoudre le nom 'blah' en a(n) 'group' component.

J'ai validé cela avec succès en utilisant Python, mais j'ai de réels problèmes avec Java 6. et Xerces 2.8.1. Quelqu'un peut-il suggérer ce qui ne va pas ici, ou une approche plus simple pour valider mes documents XML?

35
Jon

Donc, juste au cas où quelqu'un d'autre rencontrerait le même problème ici, j'avais besoin de charger un schéma parent (et des schémas enfants implicites) à partir d'un test unitaire - en tant que ressource - pour valider une chaîne XML. J'ai utilisé la Xerces XMLSchemFactory pour faire cela avec le validateur Java 6.

Afin de charger correctement le schéma enfant via un include, j'ai dû écrire un résolveur de ressources personnalisé. Le code peut être trouvé ici:

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

Pour utiliser le résolveur, spécifiez-le dans la fabrique de schémas:

xmlSchemaFactory.setResourceResolver(new ResourceResolver());

et il l'utilisera pour résoudre vos ressources via le classpath (dans mon cas depuis src/main/resources). Tous les commentaires sont les bienvenus à ce sujet ...

18
Jon

http://www.kdgregory.com/index.php?page=xml.parsing section ' Plusieurs schémas pour un seul document '

Ma solution basée sur ce document:

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"));
6
iolha

J'ai fait face au même problème et après avoir enquêté, j'ai trouvé cette solution. Ça marche pour moi.

Enum pour configurer les différents 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;
    }
}

Méthode pour valider:

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.");
}
2
Weslor

Le truc du schéma dans Xerces est (a) très, très pédant, et (b) donne des messages d'erreur totalement inutiles quand il n'aime pas ce qu'il trouve. C'est une combinaison frustrante.

Le truc du schéma dans python peut être beaucoup plus indulgent, et laissait passer de petites erreurs dans le schéma sans être signalées.

Maintenant, si, comme vous le dites, c.xsd inclut b.xsd et b.xsd inclut a.xsd, alors il n'est pas nécessaire de charger les trois dans la fabrique de schémas. Non seulement cela est inutile, mais cela risque de semer la confusion chez Xerces et de provoquer des erreurs, ce qui peut donc être votre problème. Passez simplement c.xsd à l'usine et laissez-le résoudre b.xsd et a.xsd lui-même, ce qu'il devrait faire par rapport à c.xsd.

2
skaffman

De la documentation 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);
2
Hesse

J'ai fini par utiliser ceci:

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

N'oubliez pas de changer:

1) Le paramètre "http: // your_url_schema_location" pour l'emplacement du fichier xsd.

2) La chaîne "/ home/user/myfile.xml" pour celle pointant vers votre fichier xml.

Je n'ai pas eu à définir la variable: -Djavax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema=org.Apache.xerces.jaxp.validation.XMLSchemaFactory

1
Edenshaw

Juste au cas où quelqu'un viendrait encore ici pour trouver la solution pour valider xml ou un objet contre plusieurs XSD, je le mentionne ici

//Using **URL** is the most important here. With URL, the relative paths are resolved for include, import inside the xsd file. Just get the parent level xsd here (not all included xsds).

URL xsdUrl = getClass().getClassLoader().getResource("my/parent/schema.xsd");

SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(xsdUrl);

JAXBContext jaxbContext = JAXBContext.newInstance(MyClass.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setSchema(schema);

/* If you need to validate object against xsd, uncomment this
ObjectFactory objectFactory = new ObjectFactory();
JAXBElement<MyClass> wrappedObject = objectFactory.createMyClassObject(myClassObject); 
marshaller.marshal(wrappedShipmentMessage, new DefaultHandler());
*/

unmarshaller.unmarshal(getClass().getClassLoader().getResource("your/xml/file.xml"));
0
Shafiul