Skip to content

XmlAdapter result marshaling error in case of ValueType=Object  #731

@Spikhalskiy

Description

@Spikhalskiy

Hi,

I have an error "com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class java.lang.String and no properties discovered to create BeanSerializer" in case of using custom XmlAdapter with such declaration:

public static class IntegerListXmlAdapter extends XmlAdapter<Object, List<Integer>> {
        ...
        @Override
        public Object marshal(List<Integer> list) throws Exception {
            return Joiner.on(",").join(list);
        }
}

If change declaration of this class to "extends XmlAdapter<String, List>" it works good.

Full example:

public class IntegerListXmlAdapterTest {
    @Test
    public void testBasic() throws JsonProcessingException {
        ObjectMapper mapper = (new ObjectMapper()).setAnnotationIntrospector(new JaxbAnnotationIntrospector());
        SomeIntListHolder listHolder = new SomeIntListHolder();
        listHolder.setListOne(asList(1, 2, 3));
        System.out.println(mapper.writeValueAsString(listHolder));
    }

    public static class IntegerListXmlAdapter extends XmlAdapter<Object, List<Integer>> {
        @Override
        public List<Integer> unmarshal(Object value) throws Exception {return null;}

        @Override
        public Object marshal(List<Integer> list) throws Exception {
            return Joiner.on(",").join(list);
        }
    }

    public static class IntegerListToStringXmlAdapter extends XmlAdapter<String, List<Integer>> {
        public List<Integer> unmarshal(String value) throws Exception {return null;}

        public String marshal(List<Integer> list) throws Exception {
            return Joiner.on(",").join(list);
        }
    }

    @XmlRootElement
    @XmlAccessorType(XmlAccessType.NONE)
    public static class SomeIntListHolder {

        @XmlAttribute
        @XmlJavaTypeAdapter(IntegerListXmlAdapter.class)
        private List<Integer> listOne;

        public List<Integer> getListOne() {
            return listOne;
        }

        public void setListOne(List<Integer> listOne) {
            this.listOne = listOne;
        }
    }
}

In this state with last Jackson version we will get an error

com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class java.lang.String and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: SomeIntListHolder["listOne"])
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:59)
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:26)
    at com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer.serialize(StdDelegatingSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:575)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:663)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:156)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:129)
    at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3385)
    at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:2779)

But if we change XmlJavaTypeAdapter to IntegerListToStringXmlAdapter error will be fixed and code will work fine.
This error exists only in Jackson 2, we have this code with Object generic on Jackson 1 and get an issue only during migration to new major version.

This concrete error can be fixed by hack in com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer:

    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException
    {
        Object delegateValue = convertValue(value);
        // should we accept nulls?
        if (delegateValue == null) {
            provider.defaultSerializeNull(gen);
            return;
        }

        //original code:
        //_delegateSerializer.serialize(delegateValue, gen, provider);

        JsonSerializer<Object> delegateSerializer;
        if (_delegateSerializer instanceof UnknownSerializer) {
            delegateSerializer =  provider.findValueSerializer(delegateValue.getClass());
        } else {
            delegateSerializer = _delegateSerializer;
        }

        delegateSerializer.serialize(delegateValue, gen, provider);
    }

You can find test class here: https://github.com/Spikhalskiy/jackson_xmladapter__bug/blob/master/src/test/java/IntegerListXmlAdapterTest.java

and hacked serializer code here: https://github.com/Spikhalskiy/jackson_xmladapter__bug/blob/master/src/main/java/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java

Now test passing in this repo because of fake StdDelegatingSerializer in classpath - try to delete it to get an issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions