Search code examples
javajsonjerseyjax-rsmoxy

How to map/convert nested params with jersey-media-moxy?


I'm programming a RESTful web application with jersey and MOXy for JAXB based JSON support.

My goal is to use a POJO as a method parameter that consumes JSON.

@POST
@Consumes(MediaType.APPLICATION_JSON)
public void postRequest(MyPOJO myPOJO) {
    System.out.println(myPOJO.name);
    System.out.println(myPOJO.currency);
}

This POJO has a string member and a member of type java.util.Currency.

@XmlRootElement
public class MyPOJO {
    public String name;
    public Currency currency;
    public MyPOJO() {};
}

I have also a custom Currency ParamConverter (and Provider), see below. With this I'm able to handle a QueryParam of type Currency correctly.

But if I query my resource by POST with the following JSON content currency is not mapped and will be null while name is mapped correctly.

{ "name": "peter", "currency": "EUR" }

Console output therefor results in:

peter
null

What else I have to do to tell MOXy to correctly map this nested object members?

This is my ParamConverter for Currency:

@Provider
public class CurrencyParamConverterProvider implements ParamConverterProvider {
    @Override
    public <T> ParamConverter<T> getConverter(final Class<T> rawType, Type genericType, Annotation[] annotations) {
        return rawType != Currency.class ? null : new ParamConverter<T>() {
            @Override
            public T fromString(String value) {
                if (value == null) {
                    throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value"));
                }

                try {
                    return rawType.cast(Currency.getInstance(value));
                } catch (IllegalArgumentException e) {
                    throw new ExtractorException("Currency must be a valid ISO 4217 code of the currency.", e);
                }
            }

            @Override
            public String toString(T value) {
                if (value == null) {
                    throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value"));
                }
                return value.toString();
            }
        };
    }
}

Solution

  • Currency is an object, so in the JSON world, it should be

    { "name": "peter", "currency": { "fieldName" :"EUR"} }
    

    The reason you don't get any failure notice is that MOXy by default is just meant o ignore these type of missing/unknown property issues.

    One way to get around this is to use an XmlAdapter. For example

    import javax.xml.bind.annotation.adapters.XmlAdapter;
    
    public class CurrencyAdapter extends XmlAdapter<String, Currency>{
    
        @Override
        public Currency unmarshal(String v) throws Exception {
            Currency c = new Currency();
            c.type = v;
            return c;
        }
    
        @Override
        public String marshal(Currency v) throws Exception {
            return v.type;
        }  
    }
    

    Where Currency.type is simply the field to hold the "EUR" value. Then you just need to annotate the property/field with the adapter

    @XmlRootElement
    public class MyPojo {
        public String name;
        @XmlJavaTypeAdapter(CurrencyAdapter.class)
        public Currency currency;
    }