Qual è il modo migliore per convalidare un file XML contro un file XSD?


Sto generando alcuni file xml che devono essere conformi a un file xsd che mi è stato dato. Qual è il modo migliore per verificare che siano conformi?

Author: ScanQR, 2008-08-19

13 answers

La libreria Java runtime supporta la convalida. L'ultima volta che ho controllato questo era il parser Apache Xerces sotto le coperte. Probabilmente dovresti usare un javax.XML.convalida.Validatore .

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 costante di fabbrica dello schema è la stringa http://www.w3.org/2001/XMLSchema che definisce XSD. Il codice precedente convalida un descrittore di distribuzione WAR contro l'URL http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd ma è possibile convalidare facilmente un file locale.

Non dovresti usare il DOMParser per convalidare un documento (a meno che il tuo obiettivo è comunque creare un modello a oggetti di documento). Questo inizierà a creare oggetti DOM mentre analizza il documento-uno spreco se non li userai.

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

Ecco come farlo usando Xerces2. Un tutorial per questo, qui (req. registrazione).

Attribuzione originale: palesemente copiata da qui :

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

Costruiamo il nostro progetto usando ant, quindi possiamo usare l'attività schemavalidate per controllare i nostri file di configurazione:

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

Ora i file di configurazione cattivi falliranno la nostra build!

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

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

Poiché questa è una domanda popolare, farò notare che java può anche convalidare contro "riferito a" xsd, ad esempio se il .il file xml stesso specifica XSD nell'intestazione, usando xsi:schemaLocation o xsi:noNamespaceSchemaLocation (o xsi per particolari spazi dei nomi) ex :

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

O schemaLocation (sempre un elenco di namespace alle mappature 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">
  ...

Le altre risposte funzionano anche qui, perché il .i file xsd "mappano" agli spazi dei nomi dichiarati nel .file xml, perché dichiarano uno spazio dei nomi, e se corrisponde con lo spazio dei nomi nel .file xml, sei a posto. Ma a volte è conveniente essere in grado di avere un resolver personalizzato...

Da javadocs: "Se si crea uno schema senza specificare un URL, un file o un'origine, il linguaggio Java ne crea uno che cerca nel documento convalidato per trovare lo schema che dovrebbe utilizzare. Ad esempio: "

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

E questo funziona per più spazi dei nomi, ecc. Il problema con questo approccio è che xmlsns:xsi è probabilmente un percorso di rete, quindi per impostazione predefinita uscirà e colpirà la rete con ogni convalida, non sempre ottimale.

Ecco un esempio che convalida un file XML contro qualsiasi riferimento it di XSD (anche se deve estrarli dalla rete):

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

È possibile evitare di estrarre XSD referenziati dalla rete, anche se i file xml fanno riferimento agli URL, specificando manualmente l'xsd (vedere alcune altre risposte qui) o utilizzando un "catalogo XML" risolutore di stile. Primavera apparentemente anche può intercettare le richieste URL per servire i file locali per le convalide. Oppure puoi impostare il tuo tramite setResourceResolver, es:

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

Vedi anchequi per un altro tutorial.

Credo che il default sia usare l'analisi DOM, puoi fare qualcosa di simile con il parser SAX che sta convalidando anche saxReader.setEntityResolver(your_resolver_here);

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

Utilizzando Java 7 è possibile seguire la documentazione fornita in descrizione del pacchetto .

// 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

Se si dispone di una macchina Linux, è possibile utilizzare lo strumento libero da riga di comando SAXCount. Ho trovato questo molto utile.

SAXCount -f -s -n my.xml

Convalida contro dtd e xsd. 5s per un file da 50 MB.

In debian squeeze si trova nel pacchetto "libxerces-c-samples".

La definizione di dtd e xsd deve essere in xml! Non puoi configurarli separatamente.

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

Un'altra risposta: dal momento che hai detto che devi convalidare i file che stai generando (scrivendo), potresti voler convalidare il contenuto mentre stai scrivendo, invece di scrivere prima, quindi leggere di nuovo per la convalida. Probabilmente puoi farlo con l'API JDK per la convalida Xml, se usi lo scrittore basato su SAX: in tal caso, basta collegarsi a validator chiamando 'Validator.validate (source, result)', dove la fonte proviene dal tuo writer, e il risultato è dove l'output deve andare.

In alternativa se usa Stax per scrivere contenuti (o una libreria che usa o può usare stax), Woodstox può anche supportare direttamente la convalida quando si utilizza XMLStreamWriter. Ecco un blog entry che mostra come questo è fatto:

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

Se stai generando file XML in modo programmatico, potresti voler guardare la libreria XMLBeans . Utilizzando uno strumento a riga di comando, XMLBeans genererà e impacchetterà automaticamente un set di oggetti Java basati su un XSD. È quindi possibile utilizzare questi oggetti per creare un documento XML basato su questo schema.

Ha il supporto integrato per la convalida dello schema e può convertire gli oggetti Java in un documento XML e viceversa.

Castor e JAXB sono altri Java librerie che hanno uno scopo simile a XMLBeans.

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

Con JAXB, puoi usare il codice qui sotto:

    @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

Utilizzando Woodstox , configurare il parser StAX per convalidare lo schema e analizzare l'XML.

Se vengono rilevate eccezioni, l'XML non è valido, altrimenti è valido:

// 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();
}

Nota: se è necessario convalidare più file, è necessario provare a riutilizzare XMLInputFactory e XMLValidationSchema per massimizzare le prestazioni.

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

Stai cercando uno strumento o una libreria?

Per quanto riguarda le librerie, praticamente lo standard de facto è Xerces2che ha entrambe le versioni C++e Java.

Essere pre avvertito però, si tratta di una soluzione di peso pesante. Ma poi di nuovo, convalidare XML contro i file XSD è un problema di peso piuttosto pesante.

Per quanto riguarda uno strumento per farlo per te, XMLFox sembra essere una soluzione freeware decente, ma non averlo usato personalmente non posso dire per sicuro.

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

Convalida rispetto agli schemi online

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

Convalida rispetto agli schemi locali

Validazione XML offline con Java

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

Ho dovuto convalidare un XML contro XSD solo una volta, quindi ho provato XMLFox. L'ho trovato molto confuso e strano. Le istruzioni di aiuto non sembrano corrispondere all'interfaccia.

Ho finito per usare LiquidXML Studio 2008 (v6) che era molto più facile da usare e più immediatamente familiare (l'interfaccia utente è molto simile a Visual Basic 2008 Express, che uso frequentemente). Lo svantaggio: la capacità di convalida non è nella versione gratuita, quindi ho dovuto usare la prova di 30 giorni.

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