Pregunta

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

¿Fue útil?

Solución

La biblioteca de tiempo de ejecución de Java admite la validación.La última vez que verifiqué esto era el analizador Apache Xerces debajo de las sábanas.Probablemente deberías usar un javax.xml.validación.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) {}

La constante de fábrica del esquema es la cadena. http://www.w3.org/2001/XMLSchema que define los XSD.El código anterior valida un descriptor de implementación WAR contra la URL http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd pero podrías validarlo con la misma facilidad con un archivo local.

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

Otros consejos

He aquí cómo hacerlo usando Xerces2.Un tutorial para esto, aquí (requisitoinscribirse).

Atribución original:copiado descaradamente de 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, por lo que podemos usar la tarea de validación de esquema para verificar nuestros archivos de configuración:

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

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

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

Dado que esta es una pregunta popular, señalaré que Java también puede validar contra xsd "referidos", por ejemplo, si el archivo .xml especifica XSD en el encabezado, usando xsi:SchemaLocation o xsi:noNamespaceSchemaLocation (o xsi para espacios de nombres particulares) ex:

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

o SchemaLocation (siempre una lista de espacios de nombres a asignaciones 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">
  ...

Las otras respuestas también funcionan aquí, porque los archivos .xsd "se asignan" a los espacios de nombres declarados en el archivo .xml, porque declaran un espacio de nombres, y si coincide con el espacio de nombres en el archivo .xml, está bien.Pero a veces es conveniente poder tener un personalizado solucionador...

De los javadocs:"Si crea un esquema sin especificar una URL, un archivo o una fuente, entonces el lenguaje Java crea uno que busca en el documento que se está validando el esquema que debe 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 el xmlsns:xsi Probablemente sea una ubicación de red, por lo que, de forma predeterminada, saldrá y accederá 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 con cualquier XSD al que haga referencia (incluso si tiene que extraerlo 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 extraer los XSD referenciados de la red, aunque los archivos xml hagan referencia a las URL, especificando el xsd manualmente (consulte algunas otras respuestas aquí) o utilizando un "catálogo XML" solucionador de estilo.La primavera aparentemente también puede interceptar la URL solicita servir archivos locales para validaciones.O puedes configurar el tuyo propio a través de establecerResourceResolver, 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);

Ver también aquí para otro tutorial.

Creo que lo predeterminado es usar el análisis DOM, puedes hacer algo similar con el analizador SAX que está validando también saxReader.setEntityResolver(your_resolver_here);

Al utilizar Java 7, puede seguir la documentación proporcionada en 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 utilizar la herramienta gratuita de línea de comandos SAXCount.Esto me pareció muy útil.

SAXCount -f -s -n my.xml

Se valida contra dtd y xsd.5 segundos para un archivo de 50 MB.

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

¡La definición de dtd y xsd debe estar en el xml!No puedes configurarlos por separado.

Una respuesta más:ya que dijiste que necesitas validar los archivos, estás generando (escribir), es posible que desee validar el contenido mientras escribe, en lugar de escribir primero y luego volver a leer para validarlo.Probablemente puedas hacerlo con la API JDK para la validación Xml, si usas un escritor basado en SAX:Si es así, simplemente vincule el validador llamando a 'Validator.validate(fuente, resultado)', donde la fuente proviene de su escritor y el resultado es donde debe ir la salida.

Alternativamente, si usa Stax para escribir contenido (o una biblioteca que usa o puede usar stax), maderastox También puede admitir directamente la validación cuando se utiliza XMLStreamWriter.Aquí está un Entrada de blog mostrando cómo se hace eso:

Si está generando archivos XML mediante programación, es posible que desee consultar el Frijoles XML biblioteca.Utilizando una herramienta de línea de comandos, XMLBeans generará y empaquetará automáticamente un conjunto de objetos Java basados ​​en un XSD.Luego puede utilizar estos objetos para crear un documento XML basado en este esquema.

Tiene soporte integrado para la validación de esquemas y puede convertir objetos Java en un documento XML y viceversa.

Castor y JAXB Hay otras bibliotecas de Java que tienen un propósito similar a los 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 ambos C++ y Java versiones.

Sin embargo, tenga en cuenta que es una solución pesada.Pero, de nuevo, validar XML con archivos XSD es un problema bastante pesado.

En cuanto a una herramienta para hacer esto por usted, XMLFox Parece ser una solución gratuita decente, pero al no haberla usado personalmente, no puedo decirlo con seguridad.

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<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 contra esquemas en línea

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 locales

Validación XML sin conexión con Java

Tuve que validar un XML contra XSD solo una vez, así que probé XMLFox.Lo encontré 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 de inmediato (la interfaz de usuario 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 prueba de 30 días.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top