Quelle est la meilleure façon de valider un fichier XML contre 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 leur conformité?

237
demandé sur ScanQR 2008-08-19 08:59:04

13 réponses

la bibliothèque Java runtime supporte la validation. La dernière fois que j'ai vérifié c'était l'Apache Xerces analyseur 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 schema est la chaîne de caractères http://www.w3.org/2001/XMLSchema qui définit XSDs. Le code ci-dessus valide un descripteur de déploiement de guerre par rapport à L'URL http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd , mais vous pouvez tout aussi facilement le valider par rapport à un fichier local.

vous ne devez pas utiliser le DOMParser pour valider un document (sauf si votre but est de créer un document object model). Cela va commencer à créer des objets DOM pendant qu'il analyse le document - inutile si vous n'allez pas les utiliser.

304
répondu McDowell 2017-06-14 22:29:27

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

Original attribution: manifestement copié à partir 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
répondu SCdF 2017-04-13 18:16:51

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

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

maintenant les fichiers de configuration de naughty vont échouer notre construction!

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

19
répondu chickeninabiscuit 2011-07-14 08:01:05

j'ai trouvé ce site utile, aussi.

http://www.ibm.com/developerworks/xml/library/x-javaxmlvalidapi.html

c'est celui qui a vraiment fonctionné pour moi avec un minimum de tracas.

10
répondu Michael Campbell 2009-05-08 20:11:37

Puisqu'il s'agit d'une question populaire, je voudrais également souligner que java peut valider par rapport à un "referred to" xsd, par exemple si le .le fichier xml lui-même spécifie un XSD, en utilisant xsi:SchemaLocation ou xsi:noNamespaceSchemaLocation (ou xsi pour des espaces de noms particuliers) comme indiqué ici :

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

ou SchemaLocation (toujours une liste de noms xsd mappings)

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

"Si vous créez un schéma sans spécifier de URL, fichier, ou source, alors le langage Java en crée un qui regarde dans le document en cours de validation pour trouver le schéma qu'il devrait utiliser. Par exemple: "

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

et cela fonctionne pour des espaces de noms multiples, etc. Le problème avec cette approche est que le xmlsns:xsi est probablement un emplacement réseau, donc ça va aller et frapper le réseau à chaque validation, pas toujours optimale.

voici un exemple qui valide un fichier XML contre Les références it de XSD (même si elle doit les retirer 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 de tirer des xsd référencés du réseau, même si les url de référence des fichiers xml, 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 requêtes D'URL pour servir les dossiers locaux pour les validations. Ou vous pouvez définir votre propre 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.

8
répondu rogerdpack 2017-06-14 22:30:43

en utilisant Java 7 vous pouvez suivre la documentation fournie dans Description du paquet .

// 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!
}
4
répondu Paulo Fidalgo 2013-05-13 09:40:38

si vous avez une machine Linux, vous pouvez utiliser L'outil gratuit de ligne de commande SAXCount. J'ai trouvé cela très utile.

SAXCount -f -s -n my.xml

valide contre dtd et xsd. 5s pour un fichier de 50 Mo.

dans debian squeeze, il est situé dans le paquet"libxerces-C-samples".

la définition de la dtd et xsd doit être dans le xml! Vous ne pouvez pas les configer séparément.

3
répondu jens 2012-03-22 17:01:25

une autre réponse: puisque vous avez dit que vous devez valider les fichiers vous êtes générant (écriture), vous pourriez vouloir valider le contenu pendant que vous écrivez, au lieu de la première écriture, puis la lecture de nouveau pour la validation. Vous pouvez probablement le faire avec L'API JDK pour la validation Xml, si vous utilisez Sax-based writer: si oui, liez simplement dans validator en appelant 'Validator.validate (source, résultat)', où la source provient de votre rédacteur, et le résultat est là 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 aussi prendre directement en charge la validation lors de l'utilisation de XMLStreamWriter. Voici une entrée de blog montrant comment cela est fait:

3
répondu StaxMan 2018-01-10 09:57:05

si vous générez des fichiers XML de façon programmatique, vous pouvez consulter la bibliothèque XMLBeans . À l'aide d'un outil en 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 construire un document XML basé sur ce schéma.

il a intégré le support pour la validation de schéma, et peut convertir des objets Java en un document XML et vice-versa.

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

2
répondu Todd 2009-01-28 18:06:02

vous cherchez un outil ou une bibliothèque?

en ce qui concerne les bibliothèques, la norme de facto est Xerces2 qui a à la fois C++ et Java versions.

soyez pré prévenu cependant, il s'agit d'une solution de poids lourd. Mais encore une fois, valider XML avec des fichiers XSD est un problème assez lourd.

quant à un outil pour faire cela pour vous, XMLFox semble être une solution freeware décente, mais ne l'ayant pas utilisé personnellement, je ne peux pas dire avec certitude.

0
répondu Adam 2008-08-19 05:11:15

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
    }
}
0
répondu razvanone 2017-11-27 15:40:01

la validation en ligne des schémas de

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 contre des schémas

validation XML hors ligne avec Java

0
répondu jschnasse 2018-10-09 14:23:05

j'ai dû valider un XML contre XSD juste une fois, donc j'ai essayé XMLFox. Je l'ai trouvé très confus 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'UI 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 libre, donc j'ai dû utiliser l'essai de 30 jours.

-2
répondu KnomDeGuerre 2008-10-01 17:35:54