¿Generar clases Java a partir de archivos .XSD ...?

127

Tengo un gigantesco archivo de esquema QuickBooks SDK .XSD que define las solicitudes / respuestas XML que puedo enviar / recibir desde QuickBooks.

Me gustaría poder generar fácilmente clases Java a partir de estos archivos .XSD, que luego podría usar para reunir XML a objetos Java y objetos Java a XML.

Hay una forma fácil de hacer esto...?

Idealmente, no requeriría ninguna biblioteca externa a la distribución básica de Java en tiempo de ejecución. Pero soy flexible ...

Keith Palmer Jr.
fuente
66
Si desea la generación manual, coloque el archivo .xsd en su proyecto de eclipse, haga clic con el botón derecho en el archivo, luego presione "generar"
Junchen Liu

Respuestas:

121

JAXB hace EXACTAMENTE lo que quieres. Está integrado en el JRE / JDK a partir de 1.6

basszero
fuente
77
Desafortunadamente, esta funcionalidad ya no estará disponible a partir de Java 9. Esto se debe a que las clases involucradas (en particular, las clases com.sun.tools.xjc. *) Ya no estarán disponibles a través del JDK.
Marco
44
No creo que la eliminación de esto del JDK deba ser un problema, porque el proyecto java.net (vinculado en la respuesta) probablemente se va a quedar.
stmoebius
1
Como se explica aquí , puede agregar las dependencias en Java 9 mediante un argumento de línea de comandos, o agregar la dependencia manualmente.
Matthias Ronge
118

Para ampliar los comentarios anteriores sobre "usar JAXB",

En Windows "%java_home%\bin\xjc" -p [your namespace] [xsd_file].xsd

p.ej, "%java_home%\bin\xjc" -p com.mycompany.quickbooks.obj quickbooks.xsd

Espere un poco, y si tuviera un archivo XSD bien formado, obtendrá algunas clases de Java bien formadas

Ed Norris
fuente
3
¡Muchas gracias! Para generar clases de nivel superior en lugar de encapsuladas, consulte: stackoverflow.com/questions/13175224/… . Y si conduce a conflictos de nombres de clase, consulte: stackoverflow.com/questions/13414407/…
mañana
38

Si desea comenzar a codificar Java a XML y XML a Java en menos de 5 minutos, pruebe la serialización XML simple. No pase horas aprendiendo la API JAXB http://simple.sourceforge.net/download/stream/doc/tutorial/tutorial.php

Sin embargo, si está realmente interesado en aprender JAXB, aquí hay un excelente tutorial http://blogs.oracle.com/teera/entry/jaxb_for_simple_java_xml

Contenido del tutorial:

JAXB para la serialización simple de Java-XML

Hay varias maneras de hacer la serialización XML en Java. Si desea un control detallado sobre el análisis y la serialización, puede optar por SAX, DOM o Stax para un mejor rendimiento. Sin embargo, lo que a menudo quiero hacer es un mapeo simple entre POJO y XML. Sin embargo, la creación de clases Java para realizar el análisis de eventos XML de forma manual no es trivial. Recientemente encontré que JAXB es un mapeo o serialización Java-XML rápido y conveniente.

JAXB contiene muchas características útiles, puede consultar la implementación de referencia aquí. El blog de Kohsuke también es un buen recurso para aprender más sobre JAXB. Para esta entrada de blog, le mostraré cómo hacer una simple serialización Java-XML con JAXB.

POJO a XML

Digamos que tengo un objeto Item Java. Quiero serializar un objeto Item a formato XML. Lo que tengo que hacer primero es anotar este POJO con algunas anotaciones XML del paquete javax.xml.bind.annotation. *. Consulte el listado de códigos 1 para Item.java

Del código

  • @XmlRootElement(name="Item") indica que quiero ser el elemento raíz.
  • @XmlType(propOrder = {"name", "price"}) indica el orden en que quiero que el elemento se organice en la salida XML.
  • @XmlAttribute(name="id", ...) indica que id es un atributo del elemento raíz.
  • @XmlElement(....) indica que quiero que el precio y el nombre sean elementos dentro del artículo.

Mi Item.javaesta listo. Luego puedo seguir adelante y crear un script JAXB para el artículo de cálculo de referencias.

//creating Item data object
Item item = new Item();
item.setId(2);
item.setName("Foo");
item.setPrice(200);
.....

JAXBContext context = JAXBContext.newInstance(item.getClass());
Marshaller marshaller = context.createMarshaller();
//I want to save the output file to item.xml
marshaller.marshal(item, new FileWriter("item.xml"));

Para obtener una lista completa de códigos, consulte la Lista de códigos 2 main.java. Se item.xmlcrea el archivo de salida del Listado de códigos 3 . Se parece a esto:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns1:item ns1:id="2" xmlns:ns1="http://blogs.sun.com/teera/ns/item">

 <ns1:itemName>Foo</ns1:itemName>
<ns1:price>200</ns1:price>

</ns1:item>

Fácil verdad? Alternativamente, puede canalizar el XML de salida como String de texto, Stream, Writer, ContentHandler, etc. simplemente cambiando el parámetro del método marshal (...) como

...
JAXBContext context = JAXBContext.newInstance(item.getClass());
Marshaller marshaller = context.createMarshaller();
// save xml output to the OutputStream instance
marshaller.marshal(item, <java.io.OutputStream instance>);

...
JAXBContext context = JAXBContext.newInstance(item.getClass());
Marshaller marshaller = context.createMarshaller();
StringWriter sw = new StringWriter();
//save to StringWriter, you can then call sw.toString() to get java.lang.String
marshaller.marshal(item, sw);

XML a POJO

Vamos a revertir el proceso. Suponga que ahora tengo un fragmento de datos de cadena XML y quiero convertirlo en objeto Item.java. Los datos XML (Listado de código 3) se ven como

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns1:item ns1:id="2" xmlns:ns1="http://blogs.sun.com/teera/ns/item">
<ns1:itemName>Bar</ns1:itemName>
<ns1:price>80</ns1:price>
</ns1:item>

Entonces puedo deshacer este código xml al objeto Item por

...
ByteArrayInputStream xmlContentBytes = new ByteArrayInputStream (xmlContent.getBytes());
JAXBContext context = JAXBContext.newInstance(Item.getClass());
Unmarshaller unmarshaller = context.createUnmarshaller();
//note: setting schema to null will turn validator off
unmarshaller.setSchema(null);
Object xmlObject = Item.getClass().cast(unmarshaller.unmarshal(xmlContentBytes));
return xmlObject;
...

Para obtener una lista completa de códigos, consulte la Lista de códigos 2 (main.java). La fuente XML puede venir en muchas formas, tanto de Stream como de archivo. La única diferencia, nuevamente, es el parámetro del método:

...
unmarshaller.unmarshal(new File("Item.xml")); // reading from file
...
// inputStream is an instance of java.io.InputStream, reading from stream
unmarshaller.unmarshal(inputStream);

Validación con esquema XML

Lo último que quiero mencionar aquí es validar el XML de entrada con el esquema antes de desarmar al objeto Java. Creo un archivo de esquema XML llamado item.xsd. Para obtener una lista completa de códigos, consulte la Lista de códigos 4 (Item.xsd). Ahora lo que tengo que hacer es registrar este esquema para su validación.

...
Schema schema = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
.newSchema(new File("Item.xsd"));
unmarshaller.setSchema(schema); //register item.xsd shcema for validation
...

Cuando intento descomponer datos XML en POJO, si el XML de entrada no se ajusta al esquema, se detectará una excepción. Para obtener una lista completa de códigos, consulte la Lista de códigos 5 (invalid_item.xml).

javax.xml.bind.UnmarshalException
- with linked exception:
javax.xml.bind.JAXBException caught: null
[org.xml.sax.SAXParseException: cvc-datatype-valid.1.2.1: 'item1' is
                                not a valid value for 'integer'.]

Aquí cambio el atributo 'id' a cadena en lugar de entero.

Si la entrada XML es válida contra el esquema, los datos XML se desarmarán al objeto Item.java con éxito.

Aries McRae
fuente
1
Esto parece muy prometedor, y es mucho más simple y agradable trabajar con lo que necesito que algo grande y complicado como JAXB. Desafortunadamente, no veo ninguna forma de convertir mi .XSD existente a archivos .class, que es realmente lo que necesito para comenzar. ¿Hay alguna forma de hacer esto?
Keith Palmer Jr.
77
Desafortunadamente, el blog con el Tutorial JAXB está fuera de línea.
Luis C.
podemos hacerlo fácilmente usando jaxb2-maven-plugin, consulte este tutorial journaldev.com/1312/…
Pankaj
¿Qué tiene de complicado ""% java_home% \ bin \ xjc "-p [su espacio de nombres] [xsd_file] .xsd"?
Gorefest
30

Usando Eclipse IDE: -

  1. copie el xsd en un proyecto nuevo / existente.
  2. Asegúrese de tener los JAR requeridos por JAXB en su classpath. Puedes descargar uno aquí .
  3. Haga clic derecho en el archivo XSD -> Generar -> clases JAXB.
Kumar Sambhav
fuente
17

La forma más fácil es usar la línea de comandos. Simplemente escriba en el directorio de su archivo .xsd:

xjc myFile.xsd.

Entonces, el Java generará todos los Pojos.

Crozeta
fuente
14

Maven se puede usar para este propósito, necesita agregar algunas dependencias y simplemente limpiar su aplicación. Obtendrá todas las clases creadas automáticamente en su carpeta de destino.

Simplemente cópielos del destino al lugar deseado, aquí está pom.xml que he usado para crear clasificados a partir de archivos xsd:

    <plugin>
     <groupId>org.codehaus.mojo</groupId>
     <artifactId>jaxb2-maven-plugin</artifactId>

     <executions>
      <execution>
       <goals>
        <goal>xjc</goal>
       </goals>
      </execution>
     </executions>
     <configuration>
      <schemaDirectory>src/main/webapp/schemas/</schemaDirectory>
     </configuration>
    </plugin>

   </plugins>
  </pluginManagement>
 </build>
</project>

Simplemente coloque sus archivos xsd en "src / main / webapp / schemas /" y Maven los encontrará en tiempo de compilación.

Espero que esto te ayude, puedes encontrar más información en http://www.beingjavaguys.com/2013/04/create-spring-web-services-using-maven.html

Espero que ayude :)

neel4soft
fuente
1
¿Esto realmente funciona con el directorio de recursos? (src / main / resources / schemas), porque sigo recibiendo esto:No XSD files found. Please check your plugin configuration.
zygimantus
7

Bueno, la mejor opción es %java_home%\bin\xjc -p [your namespace] [xsd_file].xsd.

También tengo una pregunta si tenemos una opción para hacer ingeniería inversa aquí. en caso afirmativo, ¿podemos generar xsd de la clase pojo?

sree
fuente
xjc solo está disponible en java6
sree
7

Si no le importa usar una biblioteca externa, he usado Castor para hacer esto en el pasado.

Marc Novakowski
fuente
Si genera código con Castor, ¿esas clases generadas siguen dependiendo de Caster después del hecho? ¿O podría mover esas clases a una máquina sin las bibliotecas Castor, y todavía funcionarían?
Keith Palmer Jr.
No, las clases generadas no dependen de las bibliotecas Castor.
Dave
¿Hay buenos tutoriales sobre cómo usar Castor para hacer esto? Parece muy prometedor ... pero Java, por decir lo menos, no es mi lenguaje más fuerte. No estoy seguro de qué archivos / paquetes de Castor necesito descargar y cómo hacer realmente la generación del código ... ¿algún ejemplo de novato paso a paso?
Keith Palmer Jr.
Consulte esta página para obtener documentación sobre cómo usar la clase Castor SourceGenerator
Marc Novakowski
2
Parece que Castor está muerto hace mucho tiempo ... Los enlaces de documentos son todos 404.
Pawel Veselov
5

Limitación de JAXB.

Trabajé en JAXB, según mi opinión, es una buena manera de tratar con datos entre objetos XML y Java. Los aspectos positivos son su mejor rendimiento y control sobre los datos durante el tiempo de ejecución. Con un buen uso de herramientas o scripts creados, se eliminarán muchos esfuerzos de codificación.

Descubrí que la parte de configuración no es una tarea inmediata, y pasé horas en la configuración del entorno de desarrollo.

Sin embargo, descarté esta solución debido a una limitación tonta que enfrenté. Mi definición de esquema XML (XSD) tiene un atributo / elemento con el nombre "valor" y que tengo que usar XSD tal como está. Esta muy pequeña restricción forzó el error de mi paso de enlace XJC con un error "Propiedad 'Valor' ya utilizado".

Esto se debe a la implementación de JAXB, el proceso de enlace intenta crear objetos Java a partir de XSD agregando pocos atributos a cada clase y uno de ellos es un atributo de valor. Cuando procesó mi XSD, se quejó de que ya hay una propiedad con ese nombre.

RAM
fuente
4

¿No es XJC de JAXB una posible respuesta a esto? Estoy tratando de lograr lo mismo. Sin embargo, todavía en la fase de "intento". Encontré XJC, así que pensé en compartir.

Niks
fuente
2

El conocido JAXB

Hay un complemento de Maven que podría hacer esto por usted en cualquier fase de compilación que desee.

Puede hacer esto de ambas maneras: xsd <-> Java

François Wauquier
fuente