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

2022-09-01 20:47:34

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

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

等等等等。目前,当序列化为 XML 时,我们得到如下结果:

<qualifier>FOO</qualifier>

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

<qualifier>1E</qualifier>

相反。我们可以用 和 来注释它,但这太乏味了 - 有些枚举最多有 30 个枚举值,手工编辑它并不好。我们也在考虑使用自定义序列化程序,但我现在想避免走这条路(但如果这是要走的路,那么我没有问题)。@XmlEnum@XmlEnumValue

任何想法如何?


答案 1

请尝试使用该机制。您可以为每个枚举类型创建一个子类,该子类知道如何将枚举封送/取消封送至 XML 或从 XML 取消映射枚举。XmlAdapterXmlAdapter

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

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

,然后在模型类中:

@XmlJavaTypeAdapter(QualifierAdapter.class)
private Qualifier qualifier;

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

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

答案 2

在寻找其他东西时发现了这个问题,但我读了你对更通用的东西的评论。以下是我一直用于将大写枚举类型转换为驼峰大小写的内容。我将使用您的类型,但将我的适配器放在上面。如您所见,您不需要引用限定符的每个实例,而只需注释枚举本身即可。enum

CamelCaseEnumAdapter可以采用任何一个,但是类必须传递给它,因此你需要有一个类来扩展它,我只是在枚举本身中使用一个私有静态类。enumenum


枚举:

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


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