Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Providing custom value serialization for enums via JAXB

Tags:

For a project I'm working on, we have a lot of enums in use. The model object itself is composed from a lot of tiny classes; this model we then serialize to our DB as XML via JAXB. Now, we want to be able to serialize our enum values using the return of a particular method in the enum; that is given:

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. Currently, when serialized to XML, we get something like:

<qualifier>FOO</qualifier> 

which is how JAXB handles it. However, we need the value to be the return of getCode(), and a whole lot of our enums do follow that convention (with a corresponding static method for lookup via code), so that the above XML fragment looks like:

<qualifier>1E</qualifier> 

instead. We can annotate it with @XmlEnum and @XmlEnumValue, but that's too tedious -- some enums have up to 30 enumerated values, and hand-editing it is not good. We're also thinking of using a custom serializer instead, but I'd like to avoid going that route for now (but if that's the way to go, then I have no problem with it).

Any ideas how?

like image 274
jmibanez Avatar asked Jan 11 '11 11:01

jmibanez


People also ask

How do you serialize an enum?

To serialize an enum constant, ObjectOutputStream writes the value returned by the enum constant's name method. To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the java.

Can enum implement serializable?

Because enums are automatically Serializable (see Javadoc API documentation for Enum), there is no need to explicitly add the "implements Serializable" clause following the enum declaration. Once this is removed, the import statement for the java. io.

Can you assign values to enums?

You can assign different values to enum member. A change in the default value of an enum member will automatically assign incremental values to the other members sequentially.


2 Answers

Try using the XmlAdapter mechanism for this. You create an XmlAdapter subclass for each enum type, and which knows how to marshal/unmarshal the enum to and from XML.

You then associate the adapter with the property, e.g.

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

and then in the model classes:

@XmlJavaTypeAdapter(QualifierAdapter.class) private Qualifier qualifier; 

You can also declare this at the package level, inside a file called package-info.java in the same package as your model classes, using the rather idiosyncratic package annotations:

@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters({   @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(     type=Qualifier.class, value=QualifierAdapter.class   ) }) package com.xyz; 
like image 65
skaffman Avatar answered Sep 29 '22 10:09

skaffman


Found this question while looking for something else but I read your comment about something more generic. Heres what I have been using to convert upper case enum types to camel case. I am going to use your enum type but put my adapter on it. As you can see you dont need to reference every instance of Qualifier but just annotate the enum itself.

The CamelCaseEnumAdapter can take any enum however the enum class must be passed to it therefore you need to have a class extend it, I just use a private static class inside the enum itself.


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();     } } 
like image 33
ug_ Avatar answered Sep 29 '22 09:09

ug_