Question

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!

Was it helpful?

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.

OTHER TIPS

You can use the EnumToStringConverter

Documentation

Example

@XStreamConverter(EnumToStringConverter.class)
public enum MyStateEnum {
enter code here

...

Use xstream.autodetectAnnotations(true)

Why are you using xstream for json support? You have a couple of other libraries specialized in json and that do it well. Also closed without quotes is not valid json.

Try for example Genson, it will work out of the box. The values in the json stream would be "Close", "Indeterminate", etc and when deserializing it will produce the correct enum.

class SomeObject {
   private MyState state;
   ...
}

Genson genson = new Genson();
// json = {"state" : "Indeterminate"}
String json = genson.serialize(new SomeObject(MyState.Indeterminate));

// deserialize back
SomeObject someObject = genson.deserialize(json, SomeObject.class);

// will print unknown
System.out.println(someObject.getDesc());
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top