Pergunta

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 eles estão em conformidade?

Foi útil?

Solução

A biblioteca de tempo de execução Java oferece suporte à validação.A última vez que verifiquei, este foi o analisador Apache Xerces nos bastidores.Você provavelmente deveria 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 string http://www.w3.org/2001/XMLSchema que define XSDs.O código acima valida um descritor de implantação WAR em relação ao URL http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd mas você pode validar facilmente em 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 - um desperdício se você não for usá-los.

Outras dicas

Veja como fazer isso usando Xerces2.Um tutorial para isso, aqui (req.inscrever-se).

Atribuição original:copiado 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.");
     }
  }
}

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

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

Agora, arquivos de configuração impertinentes irão falhar em nossa construção!

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

Como esta é uma pergunta popular, salientarei que java também pode validar em relação a xsds "referidos", por exemplo, se o próprio arquivo .xml especificar XSDs no cabeçalho, usando xsi:SchemaLocation ou 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 namespaces 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 também funcionam aqui, porque os arquivos .xsd "mapeiam" para os namespaces declarados no arquivo .xml, porque eles declaram um namespace e, se corresponderem ao namespace no arquivo .xml, você está bem.Mas às vezes é conveniente poder ter um costume resolver...

Dos javadocs:"Se você criar um esquema sem especificar uma URL, arquivo ou fonte, a linguagem Java criará um que procurará no documento que está sendo validado para encontrar o esquema que 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 com esta abordagem é que o xmlsns:xsi é provavelmente um local de rede, portanto, por padrão, ele sairá e atingirá a rede com toda e qualquer validação, nem sempre ideal.

Aqui está um exemplo que valida um arquivo XML em relação a qualquer XSD referenciado (mesmo que seja necessário extraí-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 "catálogo XML" resolvedor de estilo.Primavera aparentemente também pode interceptar as solicitações de URL para servir arquivos locais para validações.Ou você pode definir o seu próprio através 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.

Acredito que o padrão seja usar o parsing DOM, você pode fazer algo parecido com o analisador SAX que está validando também saxReader.setEntityResolver(your_resolver_here);

Usando Java 7 você pode seguir a documentação fornecida em descrição do pacote.

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

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

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

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

Mais uma resposta:já que você disse que precisa validar os arquivos, você está gerando (escrita), você pode querer validar o conteúdo enquanto escreve, em vez de primeiro escrever e depois ler novamente para validação.Você provavelmente pode fazer isso com a API JDK para validação XML, se usar um gravador baseado em SAX:nesse caso, basta vincular o validador chamando 'Validator.validate (fonte, resultado)', onde a fonte vem do seu gravador e o resultado é para onde a saída precisa ir.

Alternativamente, se você usar Stax para escrever conteúdo (ou uma biblioteca que usa ou pode usar stax), Woodstox também pode oferecer suporte direto à validação ao usar XMLStreamWriter.Aqui está um entrada do blog mostrando como isso é feito:

Se você estiver gerando arquivos XML programaticamente, você pode querer dar uma olhada no XMLBeans biblioteca.Usando uma ferramenta de linha de comando, o XMLBeans irá gerar e empacotar automaticamente um conjunto de objetos Java baseados em um XSD.Você pode então usar esses objetos para construir um documento XML baseado neste esquema.

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

rícino e JAXB são outras bibliotecas Java que servem a um propósito semelhante ao XMLBeans.

Você está procurando uma ferramenta ou uma biblioteca?

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

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

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

Com JAXB, você poderia 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
    }
}

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

Validação XML offline com Java

Tive que validar um XML em relação ao XSD apenas uma vez, então tentei o XMLFox.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 imediatamente familiar (a UI é muito semelhante ao Visual Basic 2008 Express, que uso com frequência).A desvantagem:a capacidade de validação não está na versão gratuita, então tive que usar o teste de 30 dias.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top