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


Je génère des fichiers xml qui doivent être conformes à un fichier xsd qui m'a été donné. Quelle est la meilleure façon de vérifier qu'ils sont conformes?

Author: ScanQR, 2008-08-19

13 answers

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.Validateur .

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 d'usine du schéma est la chaîne http://www.w3.org/2001/XMLSchema qui définit 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 Dompparser pour valider un document (sauf si votre objectif est de créer un modèle d'objet de document de toute façon). Cela commencera à créer des objets DOM car il analyse le document-gaspillage si vous n'allez pas les utiliser.

 341
Author: McDowell, 2017-06-14 22:29:27

Voici comment le faire en utilisant Xerces2. Un tutoriel pour cela, ici (req. de l'inscription).

Attribution originale: copiée de façon flagrante de 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.");
     }
  }
}
 25
Author: SCdF, 2017-04-13 18:16:51

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>

Maintenant, les fichiers de configuration coquins échoueront dans notre build!

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

 20
Author: chickeninabiscuit, 2011-07-14 08:01:05

Comme 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 .le fichier xml lui-même spécifie 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 aux 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 également ici, car le .les fichiers xsd" mappent " aux espaces de noms déclarés dans le .fichier xml, car ils déclarent un espace de noms, et si correspond à l'espace de noms dans le .fichier xml, vous êtes bon. Mais parfois, il est pratique de pouvoir avoir un résolveur personnalisé...

À partir des javadocs: "Si vous créez un schéma sans spécifier d'URL, de fichier ou de source, le langage Java en crée un qui regarde dans le document en cours de validation pour trouver 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 avec cette approche est que le xmlsns:xsi est probablement un emplacement réseau, donc il va par défaut sortir et frapper le réseau avec chaque validation, pas toujours optimale.

Voici un exemple qui valide un fichier XML par rapport aux références informatiques de XSD (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 référencent les URL, en spécifiant le xsd manuellement (voir d'autres réponses ici) ou en utilisant un "catalogue XML" style resolver. Printemps apparemment aussi peut intercepter les demandes d'URL pour servir des 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 aussi 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 également saxReader.setEntityResolver(your_resolver_here);

 16
Author: rogerdpack, 2020-09-10 16:56:20

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

// 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 StreamSource(new File("instance.xml"));
} catch (SAXException e) {
    // instance document is invalid!
}
 6
Author: Paulo Fidalgo, 2020-06-21 08:51:51

Si vous avez 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 contre 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 de la dtd et xsd doit être dans le xml! Vous ne pouvez pas les configurer séparément.

 3
Author: juwens, 2012-03-22 17:01:25

Une réponse de plus: puisque vous avez dit que vous devez valider les fichiers que vous générez (écriture), vous voudrez peut-être valider le contenu pendant que vous écrivez, au lieu d'écrire d'abord, puis de lire pour validation. Vous pouvez probablement le faire avec l'API JDK pour la validation Xml, si vous utilisez un écrivain basé sur SAX: si oui, il suffit de lier dans validator en appelant 'Validator.validate (source, result)', où la source provient de votre écrivain, 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 en charge directement la validation lors de l'utilisation de XMLStreamWriter. Voici une entrée de blog montrant comment cela se fait:

 3
Author: StaxMan, 2018-01-10 09:57:05

Si vous générez des fichiers XML par programme, vous pouvez regarder la bibliothèque XMLBeans . À l'aide d'un outil de ligne de commande, XMLBeans générera et empaquettera 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 un document XML et vice-versa.

Castor et JAXB autres Java bibliothèques qui servent un but similaire à XMLBeans.

 2
Author: Todd, 2009-01-28 18:06:02

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
    }
}
 2
Author: razvanone, 2017-11-27 15:40:01

En utilisant Woodstox, configurez l'analyseur StAX pour valider votre schéma et analyser le XML.

Si des exceptions sont interceptées, le XML n'est pas valide, sinon il est valide:

// create the XSD schema from your schema file
XMLValidationSchemaFactory schemaFactory = XMLValidationSchemaFactory.newInstance(XMLValidationSchema.SCHEMA_ID_W3C_SCHEMA);
XMLValidationSchema validationSchema = schemaFactory.createSchema(schemaInputStream);

// create the XML reader for your XML file
WstxInputFactory inputFactory = new WstxInputFactory();
XMLStreamReader2 xmlReader = (XMLStreamReader2) inputFactory.createXMLStreamReader(xmlInputStream);

try {
    // configure the reader to validate against the schema
    xmlReader.validateAgainst(validationSchema);

    // parse the XML
    while (xmlReader.hasNext()) {
        xmlReader.next();
    }

    // no exceptions, the XML is valid

} catch (XMLStreamException e) {

    // exceptions, the XML is not valid

} finally {
    xmlReader.close();
}

Remarque : Si vous avez besoin de valider plusieurs fichiers, vous devriez essayer de réutiliser vos XMLInputFactory et XMLValidationSchema afin de maximiser les performances.

 1
Author: Loris Securo, 2019-09-21 13:18:27

Vous cherchez un outil ou une bibliothèque?

En ce qui concerne les bibliothèques, la norme de facto est à peu près Xerces2qui a à la fois 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 le faire pour vous, XMLFox semble être une solution freeware décente, mais ne l'ayant pas utilisé personnellement, je ne peux pas dire pour assurer.

 0
Author: Adam, 2008-08-19 05:11:15

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

 0
Author: jschnasse, 2018-10-09 14:23:05

J'ai dû valider un XML contre XSD une seule fois, j'ai donc essayé XMLFox. Je l'ai trouvé 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.

 -3
Author: KnomDeGuerre, 2008-10-01 17:35:54