Java y XML con JAXB2 (V)

12:41AM dic 13, 2007 en categoria Java por Enrique Rodriguez Lasterra

Etiquetas:


En el post que toca veremos más configuraciones posibles al compilar el Schema. Hoy veremos como configurar las priopiedades/elementos y como determinar el tipo de datos con el que las clases Java almacenan los valores de los elementos y atributos del XML.

Configuración de propiedades

Si las configuraciones de clase estaban relacionadas principalmente con los tipos complejos, las configuraciones de propiedades lo hacen con los elementos más simples del Schema, element y attribute, que son traducidos a datos miembros en las clases Java generadas. Estas configuraciones se aplican con la etiqueta <xjb:property> y podemos destacar las siguiente:

  • Nombre de la propiedad, por defecto es el mismo en el schema y la clase Java, pero a través del atributo name podemos designar otro nombre.
  • generateIsSetMethod, collectionType, fixedAttributeAsConstantProperty sobreescriben el posible valor configurado en los globalBindings para la propiedad seleccionada.

Además de estos atributos, existe la etiqueta <xjs:baseType> que a su vez contiene la etiqueta <xjs:javaType> con la que podemos configurar que tipo de datos va a contener la propiedad.

Propiedades de Tipo de Datos Java

Estas propiedades de configuración permiten definir el tipo de datos java que almacenará la propiedad. Estos tipos de datos suelen ser los definidos por Java o una clase propia que debe tener un constructor vació. En ambas situaciones, JAXB crea una clase Adapter, para que podamos realizar ciertas transformaciones o realizar llamadas a operaciones de transformación al realizar la lectura y escritura del XML. Los atributos de configuración de la etiqueta <xjs:javaType> son:

  • name, nombre del tipo de datos que va a recoger el valor del XML, puede ser una clase propia de Java o una creada para al efecto.
  • xmlType, es obligatoria y solo se usa cuando la configuración javaType se aplica dentro de globalBindings, y a través de ella decidimos en que tipos de datos XML se aplicará la configuración.
  • parseMethod y printMethod son las propiedades que designan a que métodos llamará JAXB2. El valor de estas propiedades debe ser la ruta completa a la operación (paquete.clase.operación) y las operaciones serán desde el Adapter cuando cuando se realiza el marshall y unmarshall respectivamente. Las operaciones deben, en el caso del unmarshall/lectura recibir un String y retornar un instancia de la propia clase y en el caso del marshall/escritura, la inversa, recibir una instancia de la clase y retornar un String. Por supuesto estas operaciones no tienen porque estar en la clase designada en el atributo name (pensar que si se selecciona un tipo de datos java no podríais modificar la Clase)

Ejemplo: Sobre nuestro Schema base, configurar el elemento campo-uno-dos para que el dato miembro de la clase Java se llama campoDecimal, y que esta no tenga las operaciones isSet que añadimos en los globalBindings. Hacer también que el tipo de datos campo-uno-uno sea com.empresa.EnteroDecimal que cuenta con operaciones crearEnteroDecimal y escribirEntero encargadas de realizar una transformación en el tipo de datos.

Para ello, debemos realizar la siguiente configuración (modo inline) en nuestro Schema:

<xs:element name="campo-uno-uno" type="xs:int">
<xs:annotation>
<xs:documentation>
campo-uno-uno sera trasladado al tipo
com.empresa.
EnteroDecimal
</xs:documentation>
<xs:appinfo>
<jxb:property>
<jxb:baseType>
<jxb:javaType name="com.empresa.EnteroDecimal"
parseMethod="com.empresa.EnteroDecimal.crearEnteroDecimal"
printMethod="com.empresa.EnteroDecimal.escribirEntero"/>
</jxb:baseType>

</jxb:property>
</xs:appinfo>
</xs:annotation>
</xs:element>
<xs:element name="campo-uno-dos" type="xs:double" minOccurs="1">
<xs:annotation>
<xs:documentation>
Denominamos a campo-uno-dos campoDecimal y sin
operaciones is set

</xs:documentation>
<xs:appinfo>
<jxb:property name="campoDecimal"
generateIsSetMethod="false"
/>

</xs:appinfo>
</xs:annotation>
</xs:element>

La clase EnteroDecimal debe poseer los métodos indicados en la configuración y responder a las exigencia de JAXB, por tanto la clase debe tener un constructor vacío, el método crearEnteroDecimal debe ser estático, recibir un String y retornar una instancia de EnteroDecimal y el método escribirEntero la inversa. Veamoslo:

public class EnteroDecimal {
private Double decimal = new Double(0.0);
public EnteroDecimal(){}
public static EnteroDecimal crearEnteroDecimal(String valorEntero){
EnteroDecimal traducido = new EnteroDecimal();
traducido.setDecimal(new Double(valorEntero));
return traducido;
}
public static String escribirEntero(EnteroDecimal dec){
return ""+ dec.getDecimal().intValue();
}
public void setDecimal(Double decimal) {
this.decimal = decimal;
}
public Double getDecimal() {
return decimal;
}
}

Al ejecutar XJC mediante nuestra tarea de ANT, vemos los resultados:

  1. Se ha creado una clase Adapter1 dentro del paquete configurado en nuestro schemaBinding.
    package org.lasterra.jaxb2;
    import javax.xml.bind.annotation.adapters.XmlAdapter;
    import com.empresa.EnteroDecimal;
    public class Adapter1
    extends XmlAdapter<String, EnteroDecimal>
    {
    public EnteroDecimal unmarshal(String value) {
    return (com.empresa.EnteroDecimal.crearEnteroDecimal(value));
    }

    public String marshal(EnteroDecimal value) {
    return (com.empresa.EnteroDecimal.escribirEntero(value));
    }

    }
  2. La clase campoSecundario relacionada con el tipo complejo campo-uno  muestra los cambios de las configuraciones del ejemplo con varias anotaciones nuevas y la inclusión del nuevo tipo de datos EnteroDecimal para campoUnoUno y el nuevo nombre de campo-uno-dos. El método isSetCampoDecimal ya no es generado por XJC.
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "campo-uno", propOrder = {
    "campoUnoUno",
    "campoDecimal"
    })
    public class ElementoSecundario {

    @XmlElement(name = "campo-uno-uno", required = true, type = String.class)
    @XmlJavaTypeAdapter(Adapter1 .class)
    @XmlSchemaType(name = "int")

    protected EnteroDecimal campoUnoUno;
    @XmlElement(name = "campo-uno-dos")
    protected double campoDecimal;
    @XmlAttribute
    protected String attr;

    Hay que destacar como en la etiqueta XmlElement del campoUnoUno se utiliza como tipo String para que JAXB2 haga las lecturas y escrituras del XML usando este tipo, eso sí, pasando por el Adapter1. Otra nota, no he conseguido encontrar forma de renombrar la clase Adapter1 con la configuración.

La configuración externa sería la siguiente

....
<jxb:bindings node="//xs:element[@name='campo-uno-uno']">
<jxb:property>
<jxb:baseType>
<jxb:javaType name="com.empresa.EnteroDecimal"
parseMethod="com.empresa.EnteroDecimal.crearEnteroDecimal"
printMethod="com.empresa.EnteroDecimal.escribirEntero"/>
</jxb:baseType>
</jxb:property>
</jxb:bindings>
<jxb:bindings node="//xs:element[@name='campo-uno-dos']">
<jxb:property name="campoDecimal"
generateIsSetMethod="false"
/>
</jxb:bindings>
...

Hemos visto como realizar configuraciones sobre los elementos básicos del Schema, y como manejar los tipos de datos haciendo posible transformaciones entre el Schema y nuestras clases Java.

Comentarios:

Enviar un comentario:
Los comentarios han sido deshabilitados.