Quelle est la meilleure façon de valider un fichier XML par rapport à un fichier XSD ?

StackOverflow https://stackoverflow.com/questions/15732

  •  08-06-2019
  •  | 
  •  

Question

Je génère des fichiers XML qui doivent être conformes à un fichier XSD qui m'a été fourni.Quelle est la meilleure façon de vérifier leur conformité ?

Était-ce utile?

La solution

La bibliothèque d'exécution Java prend en charge la validation.La dernière fois que j'ai vérifié, c'était l'analyseur Apache Xerces sous les couvertures.Vous devriez probablement utiliser un javax.xml.validation.Validator.

import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;
import java.net.URL;
import org.xml.sax.SAXException;
//import java.io.File; // if you use File
import java.io.IOException;
...
URL schemaFile = new URL("http://host:port/filename.xsd");
// webapp example xsd: 
// URL schemaFile = new URL("http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd");
// local file example:
// File schemaFile = new File("/location/to/localfile.xsd"); // etc.
Source xmlFile = new StreamSource(new File("web.xml"));
SchemaFactory schemaFactory = SchemaFactory
    .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
try {
  Schema schema = schemaFactory.newSchema(schemaFile);
  Validator validator = schema.newValidator();
  validator.validate(xmlFile);
  System.out.println(xmlFile.getSystemId() + " is valid");
} catch (SAXException e) {
  System.out.println(xmlFile.getSystemId() + " is NOT valid reason:" + e);
} catch (IOException e) {}

La constante de la fabrique de schéma est la chaîne http://www.w3.org/2001/XMLSchema qui définit les XSD.Le code ci-dessus valide un descripteur de déploiement WAR par rapport à l'URL http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd mais vous pouvez tout aussi facilement valider par rapport à un fichier local.

Vous ne devez pas utiliser le DOMParser pour valider un document (sauf si votre objectif est de toute façon de créer un modèle objet de document).Cela commencera à créer des objets DOM au fur et à mesure de l'analyse du document - un gaspillage si vous n'allez pas les utiliser.

Autres conseils

Voici comment procéder en utilisant Xerces2.Un tutoriel pour cela, ici (demandes'inscrire).

Attribution originale :copié de manière flagrante ici:

import org.apache.xerces.parsers.DOMParser;
import java.io.File;
import org.w3c.dom.Document;

public class SchemaTest {
  public static void main (String args[]) {
      File docFile = new File("memory.xml");
      try {
        DOMParser parser = new DOMParser();
        parser.setFeature("http://xml.org/sax/features/validation", true);
        parser.setProperty(
             "http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", 
             "memory.xsd");
        ErrorChecker errors = new ErrorChecker();
        parser.setErrorHandler(errors);
        parser.parse("memory.xml");
     } catch (Exception e) {
        System.out.print("Problem parsing the file.");
     }
  }
}

Nous construisons notre projet en utilisant ant, nous pouvons donc utiliser la tâche schemavalidate pour vérifier nos fichiers de configuration :

<schemavalidate> 
    <fileset dir="${configdir}" includes="**/*.xml" />
</schemavalidate>

Désormais, les fichiers de configuration coquins feront échouer notre build !

http://ant.apache.org/manual/Tasks/schemavalidate.html

Puisqu'il s'agit d'une question populaire, je soulignerai que Java peut également valider par rapport aux XSD « référencés », par exemple si le fichier .xml lui-même spécifie les XSD dans l'en-tête, en utilisant xsi:SchemaLocation ou xsi:noNamespaceSchemaLocation (ou xsi pour des espaces de noms particuliers) ex:

<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="http://www.example.com/document.xsd">
  ...

ou SchemaLocation (toujours une liste d'espaces de noms vers des mappages xsd)

<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:SchemaLocation="http://www.example.com/my_namespace http://www.example.com/document.xsd">
  ...

Les autres réponses fonctionnent ici également, car les fichiers .xsd "mappent" sur les espaces de noms déclarés dans le fichier .xml, car ils déclarent un espace de noms, et s'ils correspondent à l'espace de noms dans le fichier .xml, tout va bien.Mais parfois c'est pratique de pouvoir avoir une coutume résolveur...

À partir des javadocs :"Si vous créez un schéma sans spécifier d'URL, de fichier ou de source, alors le langage Java en crée un qui recherche dans le document en cours de validation le schéma qu'il doit utiliser.Par exemple:"

SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = factory.newSchema();

et cela fonctionne pour plusieurs espaces de noms, etc.Le problème de cette approche est que xmlsns:xsi est probablement un emplacement réseau, donc par défaut il sortira et frappera le réseau à chaque validation, ce qui n'est pas toujours optimal.

Voici un exemple qui valide un fichier XML par rapport à tous les XSD auxquels il fait référence (même s'il doit les extraire du réseau) :

  public static void verifyValidatesInternalXsd(String filename) throws Exception {
    InputStream xmlStream = new new FileInputStream(filename);
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setValidating(true);
    factory.setNamespaceAware(true);
    factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
                 "http://www.w3.org/2001/XMLSchema");
    DocumentBuilder builder = factory.newDocumentBuilder();
    builder.setErrorHandler(new RaiseOnErrorHandler());
    builder.parse(new InputSource(xmlStream));
    xmlStream.close();
  }

  public static class RaiseOnErrorHandler implements ErrorHandler {
    public void warning(SAXParseException e) throws SAXException {
      throw new RuntimeException(e);
    }
    public void error(SAXParseException e) throws SAXException {
      throw new RuntimeException(e);
    }
    public void fatalError(SAXParseException e) throws SAXException {
      throw new RuntimeException(e);
    }
  }

Vous pouvez éviter d'extraire les XSD référencés du réseau, même si les fichiers XML font référence aux URL, en spécifiant le XSD manuellement (voir d'autres réponses ici) ou en utilisant un "catalogue XML". résolveur de style.Le printemps apparemment aussi peut intercepter l'URL demande de servir les fichiers locaux pour les validations.Ou vous pouvez définir le vôtre via setResourceResolver, ex:

Source xmlFile = new StreamSource(xmlFileLocation);
SchemaFactory schemaFactory = SchemaFactory
                                .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema();
Validator validator = schema.newValidator();
validator.setResourceResolver(new LSResourceResolver() {
  @Override
  public LSInput resolveResource(String type, String namespaceURI,
                                 String publicId, String systemId, String baseURI) {
    InputSource is = new InputSource(
                           getClass().getResourceAsStream(
                          "some_local_file_in_the_jar.xsd"));
                          // or lookup by URI, etc...
    return new Input(is); // for class Input see 
                          // https://stackoverflow.com/a/2342859/32453
  }
});
validator.validate(xmlFile);

Voir également ici pour un autre tutoriel.

Je crois que la valeur par défaut est d'utiliser l'analyse DOM, vous pouvez faire quelque chose de similaire avec l'analyseur SAX qui valide aussi saxReader.setEntityResolver(your_resolver_here);

En utilisant Java 7, vous pouvez suivre la documentation fournie dans description du forfait.

// parse an XML document into a DOM tree
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = parser.parse(new File("instance.xml"));

// create a SchemaFactory capable of understanding WXS schemas
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

// load a WXS schema, represented by a Schema instance
Source schemaFile = new StreamSource(new File("mySchema.xsd"));
Schema schema = factory.newSchema(schemaFile);

// create a Validator instance, which can be used to validate an instance document
Validator validator = schema.newValidator();

// validate the DOM tree
try {
    validator.validate(new DOMSource(document));
} catch (SAXException e) {
    // instance document is invalid!
}

Si vous disposez d'une machine Linux, vous pouvez utiliser l'outil de ligne de commande gratuit SAXCount.J'ai trouvé cela très utile.

SAXCount -f -s -n my.xml

Il valide par rapport à dtd et xsd.5s pour un fichier de 50 Mo.

Dans Debian squeeze, il se trouve dans le paquet "libxerces-c-samples".

La définition du dtd et du xsd doit être dans le XML !Vous ne pouvez pas les configurer séparément.

Encore une réponse :puisque vous avez dit que vous deviez valider les fichiers, vous êtes générateur (écriture), vous souhaiterez peut-être valider le contenu pendant que vous écrivez, au lieu de d'abord écrire, puis de le relire pour validation.Vous pouvez probablement le faire avec l'API JDK pour la validation XML, si vous utilisez un écrivain basé sur SAX :si tel est le cas, créez simplement un lien dans le validateur en appelant « Validator.validate(source, result) », où la source provient de votre rédacteur et le résultat est l'endroit où la sortie doit aller.

Alternativement, si vous utilisez Stax pour écrire du contenu (ou une bibliothèque qui utilise ou peut utiliser Stax), Woodstox peut également prendre directement en charge la validation lors de l'utilisation de XMLStreamWriter.Voici un entrée de blog montrant comment cela se fait :

Si vous générez des fichiers XML par programme, vous souhaiterez peut-être consulter le Beans XML bibliothèque.À l'aide d'un outil de ligne de commande, XMLBeans générera et conditionnera automatiquement un ensemble d'objets Java basés sur un XSD.Vous pouvez ensuite utiliser ces objets pour créer un document XML basé sur ce schéma.

Il prend en charge la validation de schéma et peut convertir des objets Java en document XML et vice versa.

Castor et JAXB sont d'autres bibliothèques Java qui remplissent un objectif similaire à XMLBeans.

Vous recherchez un outil ou une bibliothèque ?

En ce qui concerne les bibliothèques, le standard de facto est Xerces2 qui a les deux C++ et Java versions.

Attention cependant, c'est une solution lourde.Mais là encore, la validation de XML par rapport aux fichiers XSD est un problème assez lourd.

Quant à un outil pour faire cela pour vous, XMLFox semble être une solution gratuite décente, mais ne l'ayant pas utilisée personnellement, je ne peux pas en être sûr.

Avec JAXB, vous pouvez utiliser le code ci-dessous :

    @Test
public void testCheckXmlIsValidAgainstSchema() {
    logger.info("Validating an XML file against the latest schema...");

    MyValidationEventCollector vec = new MyValidationEventCollector();

    validateXmlAgainstSchema(vec, inputXmlFileName, inputXmlSchemaName, inputXmlRootClass);

    assertThat(vec.getValidationErrors().isEmpty(), is(expectedValidationResult));
}

private void validateXmlAgainstSchema(final MyValidationEventCollector vec, final String xmlFileName, final String xsdSchemaName, final Class<?> rootClass) {
    try (InputStream xmlFileIs = Thread.currentThread().getContextClassLoader().getResourceAsStream(xmlFileName);) {
        final JAXBContext jContext = JAXBContext.newInstance(rootClass);
        // Unmarshal the data from InputStream
        final Unmarshaller unmarshaller = jContext.createUnmarshaller();

        final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        final InputStream schemaAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(xsdSchemaName);
        unmarshaller.setSchema(sf.newSchema(new StreamSource(schemaAsStream)));

        unmarshaller.setEventHandler(vec);

        unmarshaller.unmarshal(new StreamSource(xmlFileIs), rootClass).getValue(); // The Document class is the root object in the XML file you want to validate

        for (String validationError : vec.getValidationErrors()) {
            logger.trace(validationError);
        }
    } catch (final Exception e) {
        logger.error("The validation of the XML file " + xmlFileName + " failed: ", e);
    }
}

class MyValidationEventCollector implements ValidationEventHandler {
    private final List<String> validationErrors;

    public MyValidationEventCollector() {
        validationErrors = new ArrayList<>();
    }

    public List<String> getValidationErrors() {
        return Collections.unmodifiableList(validationErrors);
    }

    @Override
    public boolean handleEvent(final ValidationEvent event) {
        String pattern = "line {0}, column {1}, error message {2}";
        String errorMessage = MessageFormat.format(pattern, event.getLocator().getLineNumber(), event.getLocator().getColumnNumber(),
                event.getMessage());
        if (event.getSeverity() == ValidationEvent.FATAL_ERROR) {
            validationErrors.add(errorMessage);
        }
        return true; // you collect the validation errors in a List and handle them later
    }
}

Valider par rapport aux schémas en ligne

Source xmlFile = new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream("your.xml"));
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(Thread.currentThread().getContextClassLoader().getResource("your.xsd"));
Validator validator = schema.newValidator();
validator.validate(xmlFile);

Valider par rapport aux schémas locaux

Validation XML hors ligne avec Java

J'ai dû valider un XML par rapport à XSD une seule fois, j'ai donc essayé XMLFox.J'ai trouvé cela très déroutant et bizarre.Les instructions d'aide ne semblaient pas correspondre à l'interface.

J'ai fini par utiliser LiquidXML Studio 2008 (v6) qui était beaucoup plus facile à utiliser et plus immédiatement familier (l'interface utilisateur est très similaire à Visual Basic 2008 Express, que j'utilise fréquemment).L'inconvénient :la capacité de validation n'est pas dans la version gratuite, j'ai donc dû utiliser l'essai de 30 jours.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top