javajsonenumsxstreamoxm

Custom Xstream/JSON converter for enum


I have the following Enum:

public enum MyState {
    Open("opened"),
    Close("closed"),
    Indeterminate("unknown");

    private String desc;

    private MyState(String desc) {
        setDesc(desc);
    }

    public String getDesc() {
        return this.desc;
    }

    private void setDesc(String desc) {
        this.desc = desc;
    }
}

I am trying to write an XStream Converter that will know to map back a JSON element "mystate" to a MyState instance.

"someJson": {
    "object1": {
        "mystate": closed
    }
}

This should produce, amongst other objects (someJson and object1) a MyState.Close instance. I've started the Converter, but haven't gotten very far:

public class MyStateEnumConverter implement Converter {
    @Override
    public boolean canConvert(Class clazz) {
        return clazz.equals(MyState.class);
    }

    @Override
    public void marshal(Object value, HierarchialStreamWriter writer, MarshallingContext context) {
        ??? - no clue here
    }

    @Override
    public Object unmarshal(HierarchialStreamReader reader, UnmarshallingContext context) {
        ??? - no clue here
    }
}

Then, to create the mapper and use it:

XStream mapper = new XStream(new JettisonMappedXmlDriver());
mapper.registerConverter(new MyStateEnumConverter);

SomeJson jsonObj = mapper.fromXML(jsonString);

// Should print "closed"
System.out.println(jsonObject.getObject1().getMyState().getDesc());

How can I implement marshal and unmarshal so thatI get the desired mapping? Thanks in advance!


Solution

  • You can accomplish this by doing 2 things:

    1. Adding a lookup method as well as a toString() override to your enum (MyStateEnum); and
    2. Extending XStream's AbstractSingleValueConverter instead of implementing Converter

    MyStateEnum:

    public enum MyStateEnum {
        // Everything you had is fine
    
        // But now, add:
        public static MyStateEnum getMyStateByDesc(String desc) {
            for(MyStateEnum myState : MyStateEnum.values())
                if(myState.getDesc().equals(desc))
                    return myState;
    
            return null;
        }
    
        @Override
        public String toString() {
            return getDesc();
        }
    }
    

    MyStateEnumConverter:

    public class MyStateEnumConverter extends AbstractSingleValueConverter {
        @Override
        public boolean canConvert(Class clazz) {
            return clazz.equals(MyStateEnum.class);
        }
    
        @Override
        public Object fromString(String parsedText) {
            return MyStateEnum.getMyStateByDesc(parsedText);
        }
    }
    

    By adding getMyStateByDesc(String) to your enum, you now have a way to look up all the various enumerated values from the outside, by providing a desc string. The MyStateEnumConverter (which extends AbstractSingleValueConverter) uses your toString() override under the hood to associate aMyStateEnum instance with a text string.

    So when XStream is parsing the JSON, it sees a JSON object of, say, "opened", and this new converter knows to pass "opened" into the converter's fromString(String) method, which in turn uses getMyStateByDesc(String) to lookup the appropriate enum instance.

    Don't forget to register your converter with your XStream instance as you already showed in your original question.