Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Case insensitive XMLEnumValue in JAXB

Tags:

java

enums

xml

jaxb

JAXB allows to directly locate Enum instances from XML by using the @XMLEnum annotation. It seems that JAXB is case-sensitive when it comes to locating value from @XMLEnumValue.

But I am facing a problem in which I need to locate Enum instance in a case-insensitive manner. Does JAXB in some way provide this functionality? Without this I am stuck with manually locating the Enum constant.

I can use XMLAdapter for this, but becomes very tedious to maintain an adapter for each enum created.

EDIT:

Why do you have to locate the enum in a case-insensitive manner?

Because I am using xml to read configuration for my system and I don't want users to be restricted to fixed case. I hope that answers your question.

public class CaseInsensitiveEnumAdapter<E extends Enum<E>> extends XmlAdapter<String, E> {

    private final Class<E> clazz;

    public CaseInsensitiveEnumAdapter(Class<E> clazz) {
        this.clazz = clazz;
    }

    @Override
    public E unmarshal(String v) throws Exception {
        return Enum.<E>valueOf(clazz, v.toUpperCase().trim());
    }

    @Override
    public String marshal(E v) throws Exception {
        return v.name();
    }
}

As I require the .class for an enum, I will have to create separate adapters for all enums.

like image 811
Narendra Pathai Avatar asked Jul 29 '14 07:07

Narendra Pathai


Video Answer


1 Answers

Kind Mr. Narendra,

I came up with a solution based on regular expressions. The idea is to use pattern restriction with a regular expression capable of accepting any case combination. I created an example with an enumeration of three colors: green, red and blue. The following regular exception would accept both "GrEEn" and "gReeN" literals as valid values:

g|Gr|Re|Ee|En|N

Here under the example files:

Example XML-Schema

<xs:simpleType name="ColorType" final="restriction" >
    <xs:restriction base="xs:string">
        <xs:pattern value="g|Gr|Re|Ee|En|N" />
        <xs:pattern value="r|Re|Ed|D" />
        <xs:pattern value="b|Bl|Lu|Ue|E" />
    </xs:restriction>
</xs:simpleType>
<xs:element name="SomeElement">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="Color" type="ColorType" />
        </xs:sequence>
    </xs:complexType>
</xs:element>

XML Case 1

<SomeElement>
    <Color>GreEN</Color>
</SomeElement>

XML Case 2

<SomeElement>
    <Color>gREen</Color>
</SomeElement>

JUnit

public class TestJaxb {

    private static final String GREEN = "GREEN";

    @Test
    public void shouldParseCase1() throws JAXBException, MalformedURLException {
        URL xmlUrl = // Load XML file 1
        SomeElement someElement = parse(xmlUrl, SomeElement.class);
        assertEquals(GREEN, someElement.getColor().toUpperCase());
    }

    @Test
    public void shouldParseCase2() throws JAXBException, MalformedURLException {
        URL xmlUrl = // Load XML file 2
        SomeElement someElement = parse(xmlUrl, SomeElement.class);
        assertEquals(GREEN, someElement.getColor().toUpperCase());
    }

    private <T> T parse(URL url, Class<T> clazz) throws JAXBException {
        Unmarshaller unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller();
        return clazz.cast(unmarshaller.unmarshal(url));
    }
}

I realize that this makes it more difficult for the user of your XSD specification to understand which values are allowed in the enumeration, but maybe a comment would be enough for you.

The fact is that there is no other way that I've found to achieve this without actually altering JAXB code.

Hope this helps.

Best regards

like image 154
Sergio Arrighi Avatar answered Oct 25 '22 06:10

Sergio Arrighi