通过JAXB为枚举提供自定义值序列化

jmi*_*nez 22 java xml enums jaxb xml-serialization

对于我正在进行的项目,我们使用了很多枚举.模型对象本身由许多小类组成; 然后,我们通过JAXB将此模型作为XML序列化到我们的DB.现在,我们希望能够使用枚举中特定方法的返回来序列化我们的枚举值; 给出的:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

目前,当序列化为XML时,我们得到类似的东西:

<qualifier>FOO</qualifier>
Run Code Online (Sandbox Code Playgroud)

这是JAXB处理它的方式.但是,我们需要将值作为getCode()的返回值,并且我们的许多枚举都遵循该约定(使用相应的静态方法通过代码进行查找),因此上面的XML片段如下所示:

<qualifier>1E</qualifier>
Run Code Online (Sandbox Code Playgroud)

代替.我们可以对它进行注释@XmlEnum@XmlEnumValue,但是这太乏味-一些枚举有多达30个枚举值,和手动编辑这是不好的.我们也在考虑使用自定义序列化器,但我现在想避免走那条路(但如果这是要走的路,那么我没问题).

有什么想法?

ska*_*man 24

尝试使用此XmlAdapter机制.您XmlAdapter为每个枚举类型创建一个子类,并且知道如何将枚举编组/解组到XML和从XML解组.

然后,您将适配器与属性相关联,例如

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
   }
}
Run Code Online (Sandbox Code Playgroud)

然后在模型类中:

@XmlJavaTypeAdapter(QualifierAdapter.class)
private Qualifier qualifier;
Run Code Online (Sandbox Code Playgroud)

您还可以在包级别,在package-info.java与模型类相同的包中调用的文件中,使用相当特殊的包注释声明:

@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters({
  @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(
    type=Qualifier.class, value=QualifierAdapter.class
  )
})
package com.xyz;
Run Code Online (Sandbox Code Playgroud)


ug_*_*ug_ 5

在寻找其他问题时发现了这个问题,但我读了您对更通用的问题的评论。这是我一直在使用的将大写枚举类型转换为驼峰式的方法。我将使用您的enum类型,但将适配器放在上面。如您所见,您不必引用每个Qualifier实例,而只需注释枚举本身即可。

CamelCaseEnumAdapter可以接受任何内容,enum但是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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


适配器

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();
    }
}
Run Code Online (Sandbox Code Playgroud)