I'd like to marshall/unmarshall an object containing an EnumSet field. EnumSet has no constructors so I'm trying to write an adapter for it. Of course, it should work for any EnumType so I'm trying to use generics but I don't know the exact EnumType during unmarshalling. Am I doing it the wrong way? Is there some straightforward solution?
intended usage:
@XmlJavaTypeAdapter(EnumSetAdapter.class)
private final EnumSet<MyEnumType> status;
and my attempt looks like:
import java.util.EnumSet;
import java.util.stream.Collectors;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class EnumSetAdapter<E extends Enum<E>> extends XmlAdapter<String, EnumSet<E>> {
private static final String DELIMITER = ",";
@Override
public EnumSet<E> unmarshal(String v) throws Exception {
if (v == null) {
return null;
}
String[] enumNameArr = v.split(DELIMITER);
//how to get the actual EnumType here?
List<E> enumList = Stream.of(enumNameArr).map(str -> E.valueOf(!!! enumType !!!, str))...;
EnumSet<E> resultEnumSet = EnumSet.copyOf(enumList);
return resultEnumSet;
}
@Override
public String marshal(EnumSet<E> v) throws Exception {
return (v == null) ? null : v.stream().map(e -> e.name()).collect(Collectors.joining(DELIMITER));
}
}
Thanks for any help!
Here is a partial answer...
The reason you cannot do something like
E.valueOf(...)
is becauseE
is not a concrete type; it's just a type variable, and because of type erasure, at runtime it's not known what the actual type is thatE
stands for.You would need the
Class<E>
object that specifies the type of the actual enum, but the problem is that it's not easy to get this in anXmlAdapter
. For example like this:Problem: JAXB is going to instantiate your
EnumSetAdapter
which does not give you the chance to pass the constructor parameter. So using@XmlJavaTypeAdapter(EnumSetAdapter.class)
won't work.Possible solutions: See Anyway to pass a constructor parameter to a JAXB Adapter?
One possible, but clunky solution would be to create an adapter for each specific type of enum, that extends
EnumSetAdapter
and that passes the appropriate value to its superclass constructor. For example:And then:
Disadvantage: You would need a class for each enum type.
See the question I linked to above for other possible solutions.