¿Cuál es el punto de las clases ObjectFactory de JAXB 2?

98

Soy nuevo en el uso de JAXB, y usé xjc de JAXB 2.1.3 para generar un conjunto de clases a partir de mi esquema XML. Además de generar una clase para cada elemento en mi esquema, creó una clase ObjectFactory.

No parece haber nada que me impida instanciar los elementos directamente, por ejemplo

MyElement element = new MyElement();

mientras que los tutoriales parecen preferir

MyElement element = new ObjectFactory().createMyElement();

Si miro en ObjectFactory.java, veo:

public MyElement createMyElement() {
    return new MyElement();
}

entonces cual es el trato? ¿Por qué debería molestarme en mantener la clase ObjectFactory? Supongo que también se sobrescribirá si tuviera que volver a compilar a partir de un esquema alterado.

Andrew Coleson
fuente
No estoy seguro de si se trata de un diseño previsto, pero descubrí que ObjectFactory es una clase ideal para usar en la creación de JAXBContext. Debe enumerar algunas clases allí y JAXB seguirá sus métodos, etc., por lo que son algo así como raíces. Y ObjectFactory tiene referencias a todos los elementos, por lo que basta con usar ObjectFactory.class para crear JAXBContext con todas las clases relevantes.
vbezhenar

Respuestas:

68

La compatibilidad con versiones anteriores no es la única razón. :-PAGS

Con esquemas más complicados, como los que tienen restricciones complicadas sobre los valores que puede asumir el contenido de un elemento, a veces es necesario crear JAXBElementobjetos reales . Por lo general, no son triviales de crear a mano, por lo que los create*métodos hacen el trabajo duro por usted. Ejemplo (del esquema XHTML 1.1):

@XmlElementDecl(namespace = "http://www.w3.org/1999/xhtml", name = "style", scope = XhtmlHeadType.class)
public JAXBElement<XhtmlStyleType> createXhtmlHeadTypeStyle(XhtmlStyleType value) {
    return new JAXBElement<XhtmlStyleType>(_XhtmlHeadTypeStyle_QNAME, XhtmlStyleType.class, XhtmlHeadType.class, value);
}

Así es como se coloca una <style>etiqueta en una <head>etiqueta:

ObjectFactory factory = new ObjectFactory();
XhtmlHtmlType html = factory.createXhtmlHtmlType();
XhtmlHeadType head = factory.createXhtmlHeadType();
html.setHead(head);
XhtmlStyleType style = factory.createXhtmlStyleType();
head.getContent().add(factory.createXhtmlHeadTypeStyle(style));

Los primeros tres usos de la ObjectFactorypodrían considerarse superfluos (aunque útiles para la coherencia), pero el cuarto hace que JAXB sea mucho, mucho más fácil de usar. ¡Imagina tener que escribir new JAXBElementa mano cada vez!

Chris Jester-Young
fuente
¿Puede dar un ejemplo / referencia de qué (o cuán complicado) debe ser un elemento de esquema para que create * () haga algo útil? Tengo problemas para encontrar la parte del esquema a la que hace referencia con su ejemplo JAXB. Si mi esquema se vuelve más complicado más adelante, sin duda sería bueno que create * maneje parte de él por mí, pero como es create * ni siquiera se molesta en crear subelementos por sí mismo ..
Andrew Coleson
Si descarga los archivos comprimidos de XHTML 1.1 y XHTML Modularization 1.1, encontrará directorios dentro llamados "SCHEMA". Coloque todos los archivos .xsd en los mismos directorios. Algunos de los archivos .xsd también importarán w3.org/2001/xml.xsd ; querrá ajustar las ubicaciones de manera apropiada si no desea que el archivo se descargue cada vez que ejecute xjc. [cont]
Chris Jester-Young
[cont] La parte específica del .xsd que especifica el contenido de un <head> está, en este caso, en xhtml11-model-1.xsd, en el grupo xhtml.head.content.
Chris Jester-Young
2
En cualquier caso, nadie te está apuntando con un arma a la cabeza diciendo que debes usar ObjectFactory (aunque lo encuentro útil de usar), pero cuando te encuentres con un caso en el que sea realmente útil, lo sabrás. :-)
Chris Jester-Young
¡Gracias! Supongo que mi esquema no es lo suficientemente complicado, pero lo tendré en cuenta para el futuro. :) Sabía que me tenía que perder algo.
Andrew Coleson
39

Como señaló @Chris, a veces JAXB no puede funcionar con POJO, porque el esquema no se puede asignar exactamente a Java. En estos casos,JAXBElement objetos contenedores son necesarios para proporcionar la información de tipo adicional.

Hay dos ejemplos concretos que he encontrado en los que esto es común.

  • Si desea ordenar un objeto de una clase que no tiene la @XmlRootElementanotación. Por defecto, XJC solo genera @XmlRootElementpara algunos elementos y no para otros. La lógica exacta para esto es un poco complicada, pero puede forzar a XJC a generar más @XmlRootElementclases usando el "modo de enlace simple"

  • Cuando su esquema usa grupos de sustitución. Este es un uso de esquema bastante avanzado, pero XJC traduce los grupos de sustitución a Java haciendo un uso intensivo de JAXBElementenvoltorios.

Entonces, en un modelo de objetos generado por XJC que hace un uso intensivo de JAXBElement(por cualquier motivo), necesita una forma de construir esas JAXBElementinstancias. El generado ObjectFactoryes, con mucho, la forma más fácil de hacerlo. Usted puede ellas la construcción de sí mismo, pero es torpe y propenso a errores de hacerlo.

skaffman
fuente
¡Gracias por los ejemplos adicionales!
Andrew Coleson
2
Vaya, esa es una respuesta ganadora. +1
Chris Jester-Young
Me gusta usar annox para generar el XmlRootElement como el 95% del tiempo si tengo un elemento que se refiere a un complexType, quiero XmlRootElement (bueno, más como 100% ya que no he llegado al caso de uso donde no quiero eso todavía)
Dean Hiller
9

Compatibilidad con versiones anteriores, supongo ...

http://weblogs.java.net/blog/kohsuke/archive/2005/08/a_story_of_migr.html :

... No más ObjectFactory.createXYZ. El problema con esos métodos de fábrica era que lanzaban una JAXBException marcada. Ahora puede simplemente hacer un nuevo XYZ (), no más bloques try / catch. (Lo sé, lo sé, ... esta es una de esas cosas de "¿¡qué estábamos pensando !?") ...

Bert F
fuente