2011-01-11 3 views
18

Für ein Projekt, an dem ich arbeite, haben wir viele Enums im Einsatz. Das Modellobjekt selbst besteht aus vielen kleinen Klassen; Dieses Modell wird dann über JAXB als XML in unsere DB serialisiert. Nun wollen wir in der Lage sein, unsere Enum-Werte mit der Rückkehr einer bestimmten Methode in der Enumeration zu serialisieren; das heißt gegeben:Bereitstellung von benutzerdefinierten Wert Serialisierung für Enums über 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. Wenn derzeit in XML serialisiert, bekommen wir so etwas wie:

<qualifier>FOO</qualifier> 

die ist, wie JAXB es abwickelt. Allerdings benötigen wir den Wert als Rückgabewert von getCode(), und eine ganze Reihe unserer enums folgen dieser Konvention (mit einer entsprechenden statischen Methode zum Nachschlagen über Code), so dass das obige XML-Fragment wie folgt aussieht:

<qualifier>1E</qualifier> 

stattdessen. Wir können es mit @XmlEnum und @XmlEnumValue kommentieren, aber das ist zu langweilig - einige Enums haben bis zu 30 aufgezählte Werte, und die Bearbeitung von Hand ist nicht gut. Wir denken auch darüber nach, einen angepassten Serializer zu verwenden, aber ich möchte diesen Weg vorerst vermeiden (aber wenn das der richtige Weg ist, dann habe ich kein Problem damit).

Irgendwelche Ideen wie?

Antwort

19

Versuchen Sie, den Mechanismus XmlAdapter dafür zu verwenden. Sie erstellen eine XmlAdapter-Unterklasse für jeden Enum-Typ und können die Enumeration von und nach XML marshalieren/unmarshalen.

Dann verknüpfen Sie den Adapter mit der Eigenschaft, z.

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 
    } 
} 

und dann in den Modellklassen:

@XmlJavaTypeAdapter(QualifierAdapter.class) 
private Qualifier qualifier; 

Sie können dies auch auf Paketebene erklären, innerhalb einer Datei package-info.java im selben Paket wie Ihre Modellklassen, mit dem eher eigenwilligen Paket namens Anmerkungen:

+1

Hmm ... das wie die, die wir suchen aussieht. Mein Problem ist wirklich, dass es nicht allgemein genug ist (kann Generics nicht auf der Enum verwenden), aber das ist praktikabel.Es wird besser sein, einen Adapter pro Enum zu erstellen, anstatt die Enum-Werte selbst zu kommentieren. Vielen Dank! – jmibanez

2

Diese Frage bei der Suche nach etwas anderem, aber ich lese Ihren Kommentar über etwas allgemeiner. Heres, was ich benutzt habe, um Enum-Typen in Großbuchstaben in Camel-Hüllen umzuwandeln. Ich werde deinen enum Typ benutzen, aber setze meinen Adapter darauf. Wie Sie sehen können, müssen Sie nicht auf jede Instanz von Qualifier verweisen, sondern nur die Enumeration selbst kommentieren.

Der CamelCaseEnumAdapter kann alle enum nehmen, aber die enum Klasse muss an sie übergeben werden, daher müssen Sie eine Klasse erweitern, ich verwende nur eine private statische Klasse innerhalb der Enum selbst.


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); 
     } 
    } 
} 


Adapter

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(); 
    } 
}