2011-01-11 9 views
18

Para un proyecto en el que estoy trabajando, tenemos muchas enumeraciones en uso. El objeto modelo en sí está compuesto de muchas clases pequeñas; este modelo luego lo serializamos a nuestro DB como XML a través de JAXB. Ahora, queremos poder serializar nuestros valores enum usando el retorno de un método particular en la enumeración; que se le da:Proporcionar serialización de valor personalizado para las enumeraciones a través de JAXB

public enum Qualifier { 
    FOO("1E", "Foo type document"), 
    BAR("2", "Bar object"); 

    private String code, description; 

    public Qualifier(String code, String description) { 
     this.code = code; 
     this.description = description; 
    } 

    public String getCode() { 
     return this.code; 
    } 

    public String getDescription() { 
     return this.description; 
    } 
} 

etc etc En la actualidad, cuando serializado a XML, obtenemos algo como:

<qualifier>FOO</qualifier> 

que es como JAXB maneja. Sin embargo, necesitamos que el valor sea el retorno de getCode(), y muchas de nuestras enumeraciones siguen esa convención (con un método estático correspondiente para la búsqueda mediante código), de modo que el fragmento XML anterior se vea como:

<qualifier>1E</qualifier> 

en su lugar. Podemos anotarlo con @XmlEnum y @XmlEnumValue, pero eso es demasiado tedioso: algunas enumeraciones tienen hasta 30 valores enumerados y su edición manual no es buena. También estamos pensando en usar un serializador personalizado en su lugar, pero me gustaría evitar ir por esa ruta por el momento (pero si ese es el camino a seguir, entonces no tengo ningún problema con él).

¿Alguna idea de cómo?

Respuesta

19

Intente utilizar el mecanismo XmlAdapter para esto. Crea una subclase XmlAdapter para cada tipo de enumeración, y que sabe cómo ordenar/deshacer la enum hacia y desde XML.

A continuación, asocia el adaptador con la propiedad, p.

public class QualifierAdapter extends XmlAdapter<String, Qualifier> { 

    public String marshal(Qualifier qualifier) { 
     return qualifier.getCode(); 
    } 

    public Qualifier unmarshal(String val) { 
     return Qualifier.getFromCode(val); // I assume you have a way of doing this 
    } 
} 

y luego en las clases del modelo:

@XmlJavaTypeAdapter(QualifierAdapter.class) 
private Qualifier qualifier; 

También puede declarar esto en el nivel de paquetes, dentro de un archivo llamado package-info.java en el mismo paquete que las clases del modelo, utilizando el paquete en lugar idiosincrásica anotaciones:

@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters({ 
    @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(
    type=Qualifier.class, value=QualifierAdapter.class 
) 
}) 
package com.xyz; 
+1

Hmm ... esto se parece a la que estamos buscando. Mi problema es que no es lo suficientemente general (no se pueden usar genéricos en la enumeración), pero es factible.Será mejor crear un adaptador por enum en lugar de anotar los valores enum. ¡Gracias! – jmibanez

2

Encontré esta pregunta mientras buscaba algo más pero leí tu comentario sobre algo más genérico. Aquí está lo que he estado usando para convertir los tipos de enum de mayúsculas a camel case. Voy a usar tu tipo enum pero me puse mi adaptador. Como puede ver, no necesita hacer referencia a cada instancia de Calificador, sino simplemente anotar la enumeración en sí.

El CamelCaseEnumAdapter puede tomar cualquier enum sin embargo la clase enum se debe pasar para que, por tanto, es necesario tener una clase que se aplicará, sólo tiene que utilizar una clase estática privada dentro de la propia enumeración.


Enum:

@XmlJavaTypeAdapter(Qualifier.Adapter.class) 
public enum Qualifier { 
    FOO("1E", "Foo type document"), 
    BAR("2", "Bar object"); 

    private String code, description; 

    public Qualifier(String code, String description) { 
     this.code = code; 
     this.description = description; 
    } 

    public String getCode() { 
     return this.code; 
    } 

    public String getDescription() { 
     return this.description; 
    } 

    private static class Adapter extends CamelCaseEnumAdapter<Qualifier> { 

     public Adapter() { 
      super(Qualifier.class, FOO); 
     } 
    } 
} 


adaptador

public abstract class CamelCaseEnumAdapter<E extends Enum> extends XmlAdapter<String, E>{ 

    private Class<E> clazz; 
    private E defaultValue; 

    public CamelCaseEnumAdapter(Class<E> clazz) { 
     this(clazz, null); 
    } 
    public CamelCaseEnumAdapter(Class<E> clazz, E defaultValue) { 
     this.clazz = clazz; 
     this.defaultValue = defaultValue; 
    } 

    @Override 
    @SuppressWarnings("unchecked") 
    public E unmarshal(String v) throws Exception { 
     if(v == null || v.isEmpty()) 
      return defaultValue; 
     return (E) Enum.valueOf(clazz, v.replaceAll("([a-z])([A-Z])", "$1_$2").toUpperCase()); 
    } 

    @Override 
    public String marshal(E v) throws Exception { 
     if(v == defaultValue) 
      return null; 
     return toCamelCase(v.name()); 
    } 


    private String toCamelCase(String s){ 
     String[] parts = s.split("_"); 
     String camelCaseString = ""; 
     for (String part : parts){ 
      if(camelCaseString.isEmpty()) 
       camelCaseString = camelCaseString + part.toLowerCase(); 
      else 
       camelCaseString = camelCaseString + toProperCase(part); 
     } 
     return camelCaseString; 
    } 

    private String toProperCase(String s) { 
     return s.substring(0, 1).toUpperCase() + 
        s.substring(1).toLowerCase(); 
    } 
} 
Cuestiones relacionadas