2011-01-11 8 views
18

Dla projektu, nad którym pracuję, używamy wielu wyrażeń. Sam obiekt modelu składa się z wielu małych klas; model ten następnie serializujemy do naszego DB jako XML poprzez JAXB. Teraz chcemy móc serializować nasze wartości wyliczeniowe za pomocą zwrotu konkretnej metody w wyliczeniu; że podano:Udostępnianie serializacji wartości niestandardowych dla wyliczeń za pomocą 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; 
    } 
} 

itp itd. Obecnie, gdy szeregowane do XML, otrzymujemy coś takiego:

<qualifier>FOO</qualifier> 

która jest jak JAXB obsługuje go. Jednak potrzebujemy wartości zwracanej przez getCode(), a cała masa naszych wyliczeń jest zgodna z tą konwencją (z odpowiednią statyczną metodą wyszukiwania za pomocą kodu), tak że powyższy fragment XML wygląda tak:

<qualifier>1E</qualifier> 

zamiast tego. Możemy opisać to za pomocą @XmlEnum i @XmlEnumValue, ale to zbyt męczące - niektóre wyliczenia mają do 30 wyliczonych wartości, a ręczna edycja nie jest dobra. Zastanawiamy się również nad użyciem niestandardowego serializera, ale na razie nie chciałbym iść tą drogą (ale jeśli to jest droga, to nie mam z tym problemu).

Jakieś pomysły, jak?

Odpowiedz

19

Spróbuj użyć do tego mechanizmu XmlAdapter. Tworzysz podklasę XmlAdapter dla każdego typu wyliczeniowego i która wie, jak marnować/unmarshal enum do i od XML.

Następnie należy skojarzyć adapter z właściwością, np.

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

a następnie w klasach modelowych:

@XmlJavaTypeAdapter(QualifierAdapter.class) 
private Qualifier qualifier; 

Można również zadeklarować to na poziomie pakietu, wewnątrz pliku o nazwie package-info.java w tym samym opakowaniu co klasach modelowych, przy użyciu pakietu raczej specyficzny adnotacje:

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

Hmm ... to wygląda jak ten, którego szukamy. Moim problemem jest to, że nie jest on wystarczająco ogólny (nie można używać generycznych w enumie), ale jest to wykonalne.Lepiej będzie utworzyć jeden adapter na enum, niż samemu zanotować wartości wyliczeniowe. Dzięki! – jmibanez

2

Znalazłem to pytanie, szukając czegoś innego, ale przeczytałem komentarz na temat czegoś bardziej ogólnego. Oto, czego używałem do zamiany typów wyliczenia wielkich liter na wielbłądzie. Zamierzam użyć twojego typu enum, ale umieść na nim mój adapter. Jak widać, nie musisz odwoływać się do każdego wystąpienia kwalifikatora, ale po prostu opisuj sam enum.

CamelCaseEnumAdapter zniosę enum jednak klasa enum muszą być przekazane do niego zatem trzeba mieć klasę przedłużyć go, po prostu użyć prywatną klasę statyczną wewnątrz samego wyliczenia.


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(); 
    } 
} 
Powiązane problemy