开发者

Providing custom value serialization for enums via JAXB

开发者 https://www.devze.com 2023-02-04 15:27 出处:网络
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

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 en开发者_Python百科ums 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?


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;


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

精彩评论

暂无评论...
验证码 换一张
取 消