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

263

Estou gerando alguns arquivos xml que precisam estar em conformidade com um arquivo xsd que me foi fornecido. Qual é a melhor maneira de verificar se estão em conformidade?

Jeff
fonte

Respostas:

336

A biblioteca de tempo de execução Java suporta validação. A última vez que verifiquei isso foi o analisador Apache Xerces, embaixo das cobertas. Você provavelmente deve usar um 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) {}

A constante de fábrica do esquema é a cadeia http://www.w3.org/2001/XMLSchemaque define os XSDs. O código acima valida um descritor de implementação WAR com relação ao URL, http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsdmas você pode validar facilmente com um arquivo local.

Você não deve usar o DOMParser para validar um documento (a menos que seu objetivo seja criar um modelo de objeto de documento). Isso começará a criar objetos DOM à medida que analisa o documento - desperdício se você não for usá-los.

McDowell
fonte
Você está usando um analisador DOM ou SAX neste exemplo? Como posso saber qual analisador você está usando, pois também não consigo ver uma referência.
Ziggy
1
@ziggy - este é um detalhe da implementação do JAXP . O JDK 6 da Sun usa o analisador SAX com um StreamSource . Uma implementação JAXP poderia legalmente usar um analisador DOM neste caso, mas não há motivo para isso. Se você usar um analisador DOM explicitamente para validação, você definitivamente instanciará uma árvore DOM.
21412 McDowell
Como uso um ErrorHandler com o descrito acima? É apenas o caso de criar o ErrorHandler e associá-lo ao validador? ou seja, validator.SetErrorHandler () como no exemplo nesta pergunta do SO stackoverflow.com/questions/4864681/… ?
Ziggy
As execuções não devem ser usadas apenas para situações de execução e não para o fluxo de controle?
19613 mike
Esse código não captura apenas erros fatais? Se você quiser capturar não fatais (como os não estruturais), acho que precisará usar um ErrorHandler.
Página Inicial>
25

Veja como fazer isso usando o Xerces2 . Um tutorial para isso, aqui (req. Inscrição).

Atribuição original: descaradamente copiada daqui :

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.");
     }
  }
}
SCdF
fonte
9
O analisador SAX seria mais eficiente - o analisador DOM cria objetos DOM; operações desnecessárias neste caso.
2133 McDowell
A questão é validar um XML em relação a um XSD. Nesta resposta, você está indo além e obtendo um objeto Parser, que não é necessário, certo?
29415 Weslor
"O ErrorChecker não pode ser resolvido para um tipo" .. falta importação?
Alex
20

Construímos nosso projeto usando ant, para que possamos usar a tarefa schemavalidate para verificar nossos arquivos de configuração:

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

Agora arquivos de configuração impertinentes falharão em nossa compilação!

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

chickeninabiscuit
fonte
13

Como essa é uma pergunta popular, apontarei que o java também pode validar contra xsd "referidos", por exemplo, se o próprio arquivo .xml especificar XSDs no cabeçalho, usando xsi:SchemaLocationou xsi:noNamespaceSchemaLocation(ou xsi para namespaces específicos) ex :

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

ou SchemaLocation (sempre uma lista de namespace para mapeamentos 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 funcionam aqui também, porque os arquivos .xsd "mapeiam" para os espaços de nomes declarados no arquivo .xml, porque eles declaram um espaço para nome e, se corresponder ao espaço para nome no arquivo .xml, você estará bem. Mas às vezes é conveniente poder ter um resolvedor personalizado ...

Nos javadocs: "Se você criar um esquema sem especificar uma URL, arquivo ou fonte, a linguagem Java criará uma que procure 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 isso funciona para vários namespaces, etc. O problema dessa abordagem é que xmlsns:xsiprovavelmente é um local de rede, portanto, por padrão, ele sai e atinge a rede com toda e qualquer validação, nem sempre ideal.

Aqui está um exemplo que valida um arquivo XML com relação a quaisquer XSDs que ele faz referência (mesmo que precise retirá-los 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 extrair XSDs referenciados da rede, mesmo que os arquivos xml façam referência a URLs, especificando o xsd manualmente (veja algumas outras respostas aqui) ou usando um resolvedor de estilo "Catálogo XML" . Aparentemente, o Spring também pode interceptar as solicitações de URL para servir arquivos locais para validações. Ou você pode definir o seu próprio 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);

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

rogerdpack
fonte
Não funciona para mim, o método resolveResource () não é chamado, a menos que esteja definido em schemaFactory, alguma ideia?
tomasb
Não sei, funciona para mim. Certifique-se de que você está colocando-o via setResourceResolvermas além disso, talvez aberta nova pergunta ...
rogerdpack
6

Usando o Java 7, você pode seguir a documentação fornecida na 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!
}
Paulo Fidalgo
fonte
2
"Usando o Java 7 .." Isso foi realmente incluído no Java 5 .
Andrew Thompson
4
Isso é basicamente o mesmo que a resposta aceita . Esta solução parece-me um embora ineficiente pouco, como desnecessariamente constrói o DOM para o XML para analisar: parser.parse(new File("instance.xml")). A validatoraceita um Source, para que você possa: validator.validate(new StreamSource(new File("instance.xml"))).
Alberto
Trabalhando dessa maneira, uma SAXException seria lançada no primeiro erro no arquivo xml e interromperia a validação. Mas eu quero saber todos os erros (!). Se eu usar um ErrorHandler (classe própria que implementa ErrorHandler), ele reconhecerá todos os erros, mas o bloco try-catch-validator.validate não emitirá nenhuma exceção. Como reconheço um erro na classe que chama a validação -método do meu validador? Obrigado pela ajuda!
Mrbela
Existem "erros" (por exemplo, erros de validação) e "erros fatais" (erros de formação). Um erro fatal normalmente interrompe a análise. Mas um erro de validação não o interrompe: você deve lançar explicitamente uma exceção. Portanto, é necessário fornecer um ErrorHandlerse você precisar fazer a validação.
Ludovic Kuty
1
É preciso admitir que o código parece mais limpo e fácil de ler do que a resposta aceita.
Clockwork
3

Se você possui uma máquina Linux, pode usar a ferramenta de linha de comando gratuita SAXCount. Achei isso muito útil.

SAXCount -f -s -n my.xml

Valida contra dtd e xsd. 5s para um arquivo de 50 MB.

No debian squeeze está localizado no pacote "libxerces-c-samples".

A definição de dtd e xsd deve estar no xml! Você não pode configurá-los separadamente.

juwens
fonte
2
Isto permite a validação do XML simples do vim (:! SAXCount -f -n -s%)
Shane
4
ou usar o xmllint venerável xmllint --schema phone.xsd phone.xml(a partir de uma resposta por 13ren)
rogerdpack
3

Mais uma resposta: como você disse que precisa validar os arquivos que está gerando (escrevendo), convém validar o conteúdo enquanto estiver escrevendo, em vez de escrever primeiro, e depois ler novamente para validação. Provavelmente, você pode fazer isso com a validação da API do JDK para Xml, se usar o gravador baseado em SAX: nesse caso, basta vincular o validador chamando 'Validator.validate (source, result)', onde a origem vem do seu gravador e o resultado é onde a saída precisa ir.

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

StaxMan
fonte
Ei StaxMan, existem XMLStreamWriters que imprimem recuos bonitos? Fiquei surpreso que não está na implementação padrão. Além disso, está ficando muito útil? Eu acho que é o caminho certo a seguir, mas parece muito pouco interesse nisso.
13ren
acabei de encontrar sua postagem aqui sobre o StaxMate (mas não é um XMLStreamWriter): stackoverflow.com/questions/290326/stax-xml-formatting-in-java/…
13ren
Sim, o StaxMate pode fazer isso. Ele usa XMLStreamWriter internamente para escrever conteúdo, para que você possa conectar o validador dessa maneira também.
StaxMan
2

Se você estiver gerando arquivos XML programaticamente, convém consultar a biblioteca XMLBeans . Usando uma ferramenta de linha de comando, o XMLBeans irá gerar e empacotar automaticamente um conjunto de objetos Java com base em um XSD. Você pode usar esses objetos para criar um documento XML com base neste esquema.

Possui suporte interno para validação de esquema e pode converter objetos Java em um documento XML e vice-versa.

O Castor e o JAXB são outras bibliotecas Java que servem a um propósito semelhante ao XMLBeans.

Todd
fonte
1

Com o JAXB, você pode usar 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
    }
}
razvanone
fonte
0

Você está procurando uma ferramenta ou uma biblioteca?

No que diz respeito às bibliotecas, praticamente o padrão de fato é o Xerces2, que possui as versões C ++ e Java .

Esteja avisado, porém, é uma solução de peso pesado. Mas, novamente, validar XML contra arquivos XSD é um problema bastante pesado.

Quanto a uma ferramenta para fazer isso por você, o XMLFox parece ser uma solução freeware decente, mas não tendo usado pessoalmente, não posso dizer com certeza.

Adão
fonte
0

Validar contra 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 contra esquemas locais

Validação XML offline com Java

jschnasse
fonte
0

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

Se exceções forem capturadas, 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 você precisar validar vários arquivos, tente reutilizar seu XMLInputFactorye XMLValidationSchemapara maximizar o desempenho.

Loris Securo
fonte
-3

Eu tive que validar um XML contra XSD apenas uma vez, então tentei o XMLFox. Eu achei muito confuso e estranho. As instruções de ajuda não pareciam corresponder à interface.

Acabei usando o LiquidXML Studio 2008 (v6), que era muito mais fácil de usar e mais familiarizado imediatamente (a interface do usuário é muito semelhante ao Visual Basic 2008 Express, que eu uso frequentemente). A desvantagem: o recurso de validação não está na versão gratuita, então tive que usar o teste de 30 dias.

KnomDeGuerre
fonte
1
A questão é Java, mas esta resposta não é. :-(
james.garriss
Para ser justo, a palavra "java" nunca aparece na pergunta, apenas nas tags. Eu ding a pergunta para isso, não a resposta.
precisa
Obrigado James e Mark, me ajudem a afiar!
Knom