¿Cuál es la mejor manera de validar un archivo XML contra un archivo XSD?

Estoy generando algunos archivos xml que deben ajustarse a un archivo xsd que me fue entregado. ¿Cuál es la mejor manera de verificar que se ajusten?

La biblioteca de tiempo de ejecución de Java admite validación. La última vez que revisé esto fue el analizador Apache Xerces bajo las sábanas. Probablemente deberías usar un 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 constante de fábrica del esquema es la cadena http://www.w3.org/2001/XMLSchema que define XSD. El código anterior valida un descriptor de despliegue de WAR contra la URL http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd pero también podría validar con un archivo local.

No debe usar DOMParser para validar un documento (a menos que su objective sea crear un modelo de objeto de documento de todos modos). Esto comenzará a crear objetos DOM a medida que analiza el documento, un desperdicio si no los va a usar.

Así es cómo hacerlo usando Xerces2 . Un tutorial para esto, aquí (registro requerido).

Atribución original: descaradamente copiada desde aquí :

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

Construimos nuestro proyecto usando ant, para que podamos usar la tarea schemavalidate para verificar nuestros archivos de configuración:

    

¡Ahora los archivos de configuración traviesos fallarán en nuestra construcción!

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

Encontré que este sitio también es útil.

http://www.ibm.com/developerworks/xml/library/x-javaxmlvalidapi.html

Es el que realmente funcionó para mí con un mínimo de alboroto.

Dado que esta es una pregunta popular, también me gustaría señalar que java puede validar contra un xsd “referido”, por ejemplo, si el archivo .xml mismo especifica un XSD, usando xsi:SchemaLocation o xsi:noNamespaceSchemaLocation (o xsi para espacios de nombres particulares) como se indica aquí :

  ... 

o SchemaLocation (siempre una lista de asignaciones de espacio de nombres a xsd)

  ... 

“Si crea un esquema sin especificar una URL, archivo o fuente, entonces el lenguaje Java crea uno que busca en el documento validado para encontrar el esquema que debería usar. Por ejemplo:”

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

y esto funciona para múltiples espacios de nombres, etc. El problema con este enfoque es que xmlsns:xsi es probablemente una ubicación de red, por lo que saldrá y llegará a la red con todas y cada una de las validaciones, lo que no siempre es óptimo.

Aquí hay un ejemplo que valida un archivo XML contra cualquier XSD al que haga referencia (incluso si tiene que extraerlos de la red):

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

Puede evitar sacar XSD referenciados de la red, incluso si los archivos xml hacen referencia a url, especificando el xsd manualmente (vea algunas otras respuestas aquí) o utilizando un sistema de resolución de estilo “catálogo XML”. Spring aparentemente también puede interceptar las solicitudes de URL para servir archivos locales para validaciones. O puede configurar el suyo a través de setResourceResolver , por ejemplo:

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

Ver también aquí para otro tutorial.

Usando Java 7 puede seguir la documentación provista en la descripción del paquete .

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

Si tiene una máquina Linux, puede usar la herramienta de línea de comandos gratuita SAXCount. Encontré esto muy útil.

 SAXCount -f -s -n my.xml 

Valida contra dtd y xsd. 5s para un archivo de 50MB.

En debian squeeze se encuentra en el paquete “libxerces-c-samples”.

La definición de dtd y xsd tiene que estar en xml! No puedes configurarlos por separado.

Una respuesta más: desde que dijiste que necesitas validar los archivos que estás generando (escribiendo), es posible que quieras validar el contenido mientras escribes, en lugar de escribir primero, y luego volver a leer para la validación. Probablemente pueda hacer eso con JDK API para la validación de Xml, si utiliza el escritor basado en SAX: si es así, simplemente enlace en el validador llamando a ‘Validator.validate (source, result)’, donde la fuente proviene de su escritor, y el resultado es donde la producción necesita ir.

Alternativamente, si usa Stax para escribir contenido (o una biblioteca que usa o puede usar stax), Woodstox también puede respaldar directamente la validación al usar XMLStreamWriter. Aquí hay una entrada de blog que muestra cómo se hace eso:

Si está generando archivos XML programáticamente, es posible que desee consultar la biblioteca XMLBeans . Utilizando una herramienta de línea de comandos, XMLBeans generará y empaquetará automáticamente un conjunto de objetos Java basados ​​en un XSD. A continuación, puede usar estos objetos para comstackr un documento XML basado en este esquema.

Tiene soporte integrado para validación de esquema, y ​​puede convertir objetos Java en un documento XML y viceversa.

Castor y JAXB son otras bibliotecas de Java que tienen un propósito similar a XMLBeans.

¿Estás buscando una herramienta o una biblioteca?

En lo que respecta a las bibliotecas, prácticamente el estándar de facto es Xerces2, que tiene versiones en C ++ y Java .

Antes de advertir, es una solución de gran peso. Pero, de nuevo, validar XML contra archivos XSD es un problema de gran peso.

En cuanto a una herramienta para hacer esto por usted, XMLFox parece ser una buena solución de software gratuito, pero al no haberlo usado personalmente, no puedo asegurarlo.

Con JAXB, puedes usar el siguiente código:

  @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 validationErrors; public MyValidationEventCollector() { validationErrors = new ArrayList<>(); } public List 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 } } 

Tuve que validar un XML contra XSD solo una vez, así que probé XMLFox. Encontré que es muy confuso y extraño. Las instrucciones de ayuda no parecían coincidir con la interfaz.

Terminé usando LiquidXML Studio 2008 (v6), que era mucho más fácil de usar y más familiar para mí (la IU es muy similar a Visual Basic 2008 Express, que uso con frecuencia). El inconveniente: la capacidad de validación no está en la versión gratuita, así que tuve que usar la versión de prueba de 30 días.