Domanda

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

È stato utile?

Soluzione

La libreria runtime Java supporta la convalida.L'ultima volta che ho controllato questo era il parser Apache Xerces sotto le coperte.Probabilmente dovresti usare a 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 costante della factory dello schema è la stringa http://www.w3.org/2001/XMLSchema che definisce gli XSD.Il codice precedente convalida un descrittore di distribuzione WAR rispetto all'URL http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd ma potresti facilmente eseguire la convalida rispetto a un file locale.

Non dovresti utilizzare DOMParser per convalidare un documento (a meno che il tuo obiettivo non sia comunque creare un modello a oggetti del documento).Questo inizierà a creare oggetti DOM mentre analizza il documento: uno spreco se non li utilizzerai.

Altri suggerimenti

Ecco come farlo utilizzando Xerces2.Un tutorial per questo, Qui (richiestoiscrizione).

Attribuzione originale:palesemente copiato 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.");
     }
  }
}

Costruiamo il nostro progetto utilizzando ant, quindi possiamo utilizzare 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 creazione!

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

Poiché questa è una domanda popolare, sottolineerò che Java può anche convalidare rispetto agli xsd "riferiti a", ad esempio se il file .xml stesso specifica gli XSD nell'intestazione, utilizzando xsi:SchemaLocation O xsi:noNamespaceSchemaLocation (o xsi per spazi dei nomi particolari) 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 per 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é i file .xsd "mappano" agli spazi dei nomi dichiarati nel file .xml, perché dichiarano uno spazio dei nomi e se corrisponde allo spazio dei nomi nel file .xml, sei a posto.Ma a volte è conveniente poter avere una consuetudine risolutore...

Dai javadoc:"Se crei uno schema senza specificare un URL, un file o un'origine, il linguaggio Java ne crea uno che cerca nel documento in corso di convalida per trovare lo schema da utilizzare.Per 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 singola convalida, non sempre ottimale.

Ecco un esempio che convalida un file XML rispetto a qualsiasi XSD a cui fa riferimento (anche se deve estrarlo 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);
    }
  }

Puoi evitare di estrarre gli XSD di riferimento dalla rete, anche se i file XML fanno riferimento agli URL, specificando manualmente l'XSD (vedi alcune altre risposte qui) o utilizzando un "catalogo XML" risolutore di stile.A quanto pare anche la primavera può intercettare l'URL richiede di servire i file locali per le convalide.Oppure puoi impostarne uno 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);

Guarda anche Qui per un altro tutorial.

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

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

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

Se hai una macchina Linux puoi utilizzare lo strumento gratuito da riga di comando SAXCount.L'ho trovato molto utile.

SAXCount -f -s -n my.xml

Valida rispetto a dtd e xsd.5 secondi per un file da 50 MB.

In Debian Squeeze si trova nel pacchetto "libxerces-c-samples".

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

Un'altra risposta:dal momento che hai detto che devi convalidare i file, lo sei generando (scrittura), potresti voler convalidare il contenuto mentre scrivi, invece di scrivere prima e poi rileggere per la convalida.Probabilmente puoi farlo con l'API JDK per la convalida Xml, se usi un writer basato su SAX:in tal caso, collega semplicemente il validatore chiamando 'Validator.validate(source, result)', dove source proviene dal tuo scrittore e result è dove deve andare l'output.

In alternativa, se utilizzi Stax per scrivere contenuti (o una libreria che utilizza o può utilizzare Stax), Woodstox può anche supportare direttamente la convalida quando si utilizza XMLStreamWriter.Ecco un voce del blog mostrando come è fatto:

Se stai generando file XML in modo programmatico, potresti voler guardare il file XMLBeans biblioteca.Utilizzando uno strumento da riga di comando, XMLBeans genererà e impacchetterà automaticamente una serie 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 oggetti Java in un documento XML e viceversa.

Castore E JAXB sono altre librerie Java che hanno uno scopo simile a XMLBeans.

Stai cercando uno strumento o una libreria?

Per quanto riguarda le biblioteche, praticamente lo standard di fatto lo è Xerces2 che ha entrambi C++ E Giava versioni.

Attenzione però, è una soluzione pesante.Ma ancora una volta, la convalida dell'XML rispetto ai file XSD è un problema piuttosto gravoso.

Per quanto riguarda uno strumento che possa farlo per te, XMLFox sembra essere una soluzione freeware decente, ma non avendola usata personalmente non posso dirlo con certezza.

Con JAXB, potresti utilizzare il codice seguente:

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

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

Convalidare rispetto agli schemi locali

Convalida XML offline con Java

Ho dovuto convalidare un XML rispetto a XSD solo una volta, quindi ho provato XMLFox.L'ho trovato molto confuso e strano.Le istruzioni della guida non sembravano corrispondere all'interfaccia.

Alla fine ho utilizzato 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 utilizzo frequentemente).Lo svantaggio:la funzionalità di convalida non è presente nella versione gratuita, quindi ho dovuto utilizzare la prova di 30 giorni.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top