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

Java 通过JAXB为枚举提供自定义值序列化,java,xml,enums,xml-serialization,jaxb,Java,Xml,Enums,Xml Serialization,Jaxb,对于我正在进行的一个项目,我们使用了很多枚举。模型对象本身由许多小类组成;然后,我们通过JAXB将该模型序列化为XML。现在,我们希望能够使用枚举中特定方法的返回来序列化枚举值;即: public enum Qualifier { FOO("1E", "Foo type document"), BAR("2", "Bar object"); private String code, description; public Qualifier(String co

对于我正在进行的一个项目,我们使用了很多枚举。模型对象本身由许多小类组成;然后,我们通过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>
FOO
这就是JAXB处理它的方式。但是,我们需要将该值作为getCode()的返回值,并且我们的许多枚举都遵循该约定(使用相应的静态方法通过代码进行查找),因此上面的XML片段如下所示:

<qualifier>1E</qualifier>
1E
相反。我们可以用
@XmlEnum
@XmlEnumValue
对其进行注释,但这太繁琐了——有些枚举最多有30个枚举值,手工编辑并不好。我们也在考虑改用自定义序列化程序,但我现在不想走这条路(但如果是这样的话,那么我对它没有问题)


有什么想法吗?

尝试使用
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
   }
}
您还可以在包级别,在名为
package info.java
的文件中,在与您的模型类相同的包中,使用非常特殊的包注释来声明这一点:

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

在查找其他内容时发现此问题,但我阅读了您关于更一般的内容的评论。下面是我用来将大写枚举类型转换为驼峰大小写的内容。我将使用您的
enum
类型,但将适配器放在上面。正如您所看到的,您不需要引用限定符的每个实例,只需要注释枚举本身

CamelCaseEnumAdapter可以接受任何
enum
,但是必须将
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);
        }
    }
}
@XmlJavaTypeAdapter(Qualifier.Adapter.class)
公共枚举限定符{
FOO(“1E”、“FOO类型文件”),
酒吧(“2”,“酒吧对象”);
私有字符串代码,描述;
公共限定符(字符串代码、字符串描述){
this.code=代码;
this.description=描述;
}
公共字符串getCode(){
返回此.code;
}
公共字符串getDescription(){
返回此.description;
}
私有静态类适配器扩展了私有静态类适配器{
公共适配器(){
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();
    }
}
公共抽象类CamelCaseEnumAdapter扩展了XmlAdapter{
私人课堂;
私人价值;
公共适配器(clazz类){
这个(clazz,null);
}
公共适配器(类clazz,E defaultValue){
this.clazz=clazz;
this.defaultValue=defaultValue;
}
@凌驾
@抑制警告(“未选中”)
public E unmarshal(字符串v)引发异常{
if(v==null | | v.isEmpty())
返回默认值;
返回(E)枚举值(clazz,v.replaceAll(“([a-z])([a-z])”,“$1_$2”).toUpperCase());
}
@凌驾
公共字符串封送处理(EV)引发异常{
如果(v==默认值)
返回null;
返回到CamelCase(v.name());
}
私有字符串到CAMELCASE(字符串s){
字符串[]部分=s.split(“”);
字符串camelCaseString=“”;
用于(字符串部分:部分){
if(camelCaseString.isEmpty())
camelCaseString=camelCaseString+part.toLowerCase();
其他的
camelCaseString=camelCaseString+toProperCase(部分);
}
返回字符串;
}
私有字符串toProperCase(字符串s){
返回s.substring(0,1).toUpperCase()+
s、 子字符串(1).toLowerCase();
}
}

Hmm。。。这看起来像我们要找的那个。我的问题是它不够通用(不能在枚举上使用泛型),但这是可行的。最好为每个枚举创建一个适配器,而不是注释枚举值本身。谢谢
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();
    }
}