Qual é a melhor maneira de validar um arquivo XML contra um arquivo XSD?

Estou a gerar alguns ficheiros xml que precisam de estar em conformidade com um ficheiro xsd que me foi dado. Qual é a melhor maneira de verificar se estão em conformidade?

Author: ScanQR, 2008-08-19

13 answers

A biblioteca Java runtime suporta a validação. Da última vez que verifiquei, este era o analisador Apache Xerces debaixo dos cobertores. Devias usar um javax.xml.Validacao.Validador .

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

A constante de fábrica do esquema é a string http://www.w3.org/2001/XMLSchema que define XSDs. O código acima valida um descritor de implantação de guerra contra o URL http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd, mas você poderia facilmente validar com um arquivo local.

Não deve usar o DOMParser para validar um documento (a menos que o seu o objetivo é criar um modelo de objeto de documento de qualquer forma). Isto vai começar a criar objetos DOM à medida que analisa o documento - desperdiçador se você não vai usá-los.

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

Aqui está como fazê-lo usando Xerces2. Um tutorial para isso, AQUI (req. inscricao).

Atribuição Original: copiada descaradamente de Aqui:
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

Construímos o nosso projecto usando o ant, para que possamos usar a tarefa esquemavalidate para verificar os nossos ficheiros de configuração:

<schemavalidate> 
    <fileset dir="${configdir}" includes="**/*.xml" />
</schemavalidate>
Agora os ficheiros marotos vão falhar na nossa compilação!

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

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

Uma vez que esta é uma questão popular, eu vou apontar que java também pode validar contra "referred to" xsd's, por exemplo, se o .o ficheiro xml indica o XSD no cabeçalho, usando xsi:schemaLocation ou xsi:noNamespaceSchemaLocation (ou xsi para espaços de nomes específicos) ex:

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

Ou esquemalocation (sempre uma lista de espaços de nomes para mapeamentos do 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">
  ...
As outras respostas também funcionam aqui, porque ... XSD files " map " to the namespaces declared in the .ficheiro xml, porque declaram um namespace, and if matches up with the namespace in the .ficheiro xml, estás bem. Mas às vezes é conveniente ser capaz de ter uma solução personalizada ...

A partir dos javadocs: "se você criar um esquema sem especificar um URL, arquivo ou fonte, então a linguagem Java cria um que procura no documento que está sendo validado para encontrar o esquema que ele deve usar. Por exemplo:"

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

E isto funciona para múltiplos espaços de nomes, etc. O problema com esta abordagem é que o xmlsns:xsi é provavelmente uma localização de rede, então por padrão vai sair e atingir a rede com cada validação, nem sempre o ideal.

Aqui está um exemplo que valida um ficheiro XML com qualquer referência de TI do XSD (mesmo que tenha de os retirar da rede):

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

Você pode evitar puxar os url 's de referência do XSD da rede, mesmo que os url' s de referência dos ficheiros xml, especificando o xsd manualmente (veja algumas outras respostas aqui) ou usando um estilo "XML catalog" resolver . A mola aparentemente também pode interceptar Os pedidos de URL para servir ficheiros locais para validações. Ou pode escolher a sua própria via definir a sua solução , 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);

Veja também aqui para outro tutorial.

Eu acredito que o padrão é usar a análise DOM, você pode fazer algo semelhante com o analisador SAX que está validando também saxReader.setEntityResolver(your_resolver_here);

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

Usando o Java 7, poderá seguir a documentação fornecida em Descrição do pacote .

// 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 tiver uma máquina Linux, pode usar o SAXCount da linha de comandos livre. Achei isto muito útil.

SAXCount -f -s -n my.xml

Valida contra dtd e xsd. 5s por um ficheiro de 50MB.

In debian squeeze it is located in the package "libxerces-c-samples".

A definição do dtd e do xsd tem de estar no xml! Não podes confundi-los separadamente.

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

Mais uma resposta: dado que disse que precisa de validar os ficheiros, está a gerar [3]} (escrita), poderá querer validar o conteúdo enquanto está a escrever, em vez de escrever primeiro, e depois ler de volta para validação. Você provavelmente pode fazer isso com a API JDK para validação de Xml, se você usar o escritor SAX-based: se assim for, basta ligar em validador chamando 'Validator.validate (fonte, resultado)', onde o código vem do seu escritor, e o resultado é onde o resultado precisa ir.

Em alternativa se usar o Stax para escrever o conteúdo (ou uma biblioteca que usa ou pode usar o stax), O Woodstox também poderá suportar directamente a validação ao usar o XMLStreamWriter. Aqui está uma entrada no blog mostrando como isso é feito:

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

Se estiver a gerar ficheiros XML programaticamente, poderá querer ver a biblioteca XMLBeans . Usando uma ferramenta de linha de comandos, o XMLBeans irá gerar e embalar automaticamente um conjunto de objetos Java com base em um XSD. Você pode então usar esses objetos para construir um documento XML com base neste esquema.

Ele tem suporte embutido para a validação do esquema, e pode converter objetos Java para um documento XML e vice-versa.

Castor e JAXB são outros Java bibliotecas que servem um propósito similar ao XMLBeans.

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

Com o JAXB, pode utilizar o código abaixo:

    @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

Usando Woodstox , configure o analisador de StAX para validar com o seu esquema e analisar o XML.

Se forem capturadas excepções, o XML não é válido, caso contrário é válido:

// 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 precisar de validar vários ficheiros, deve tentar reutilizar o seu XMLInputFactory e XMLValidationSchema para maximizar o desempenho.

 1
Author: Loris Securo, 2019-09-21 13:18:27
Estás à procura de uma ferramenta ou de uma biblioteca?

No que diz respeito às bibliotecas, basicamente a norma de facto é Xerces2 que tem ambas as versões C++ e Java.

Mas aviso-te que é uma solução pesada. Mas, novamente, validar XML contra arquivos XSD é um problema de peso bastante pesado.

Quanto a uma ferramenta para fazer isso para você, XMLFox parece ser uma solução decente de freeware, mas não tendo usado pessoalmente eu não posso podes ter a certeza.

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

Validar com esquemas '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);

Validar com os esquemas locais

Validação de XML Offline com Java

 0
Author: jschnasse, 2018-10-09 14:23:05
Tive de validar um XML contra o XSD só uma vez, por isso tentei o XMLFox. Achei muito confuso e estranho. As instruções de ajuda não pareciam corresponder à interface.

Acabei por usar o LiquidXML Studio 2008 (v6), que era muito mais fácil de usar e mais imediatamente familiar (a IU é muito semelhante ao Visual Basic 2008 Express, que eu uso frequentemente). A desvantagem: a capacidade de validação não está na versão livre, então eu tive que usar o teste de 30 dias.

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