8

When I use Jackson polymorphic serialization, it generates a JSON object with an incorrect fully qualified class name.

The code below serializes an XMLGregorianCalendar. The output is:

["java.util.GregorianCalendar",-3600000]

I expected the following:

["javax.xml.datatype.XMLGregorianCalendar",-3600000]

Why does it output java.util.GregorianCalendar?

Or more importantly: How do I fix this?

Code example:

import com.fasterxml.jackson.databind.ObjectMapper;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import java.io.ByteArrayOutputStream;

public class JacksonGregorianProblem {

    public static void main(String[] args) throws java.io.IOException, DatatypeConfigurationException {

        XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar();
        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        mapper.writeValue(byteArrayOutputStream, xmlGregorianCalendar);

        System.out.println(byteArrayOutputStream);
    }
}
Bouke Nijhuis
  • 349
  • 2
  • 7
  • After checking the source code of Jackson I understand why it shows the unexpected behavior. In the com.fasterxml.jackson.databind.ext.XMLGregorianCalendarSerializer class the XMLGregorianCalendar is cast to a GregorianCalendar. https://github.com/FasterXML/jackson-databind/blob/8d3d3b7d04e0c7b079bb0927e1f04b345691bba7/src/main/java/com/fasterxml/jackson/databind/ext/XMLGregorianCalendarSerializer.java So this explains why the unexpected behavior occurs. – Bouke Nijhuis Mar 16 '18 at 07:48

2 Answers2

7

To get the expected behavior I have implemented a custom XMLGregorianCalendar serializer. This class takes care of the serialization of the XLMGregorianCalendar and now the output is exactly what I expect. :-)

class XMLGregorianCalendarSerializer extends StdSerializer<XMLGregorianCalendar> {

    public XMLGregorianCalendarSerializer() {
        this(null);
    }

    public XMLGregorianCalendarSerializer(Class<XMLGregorianCalendar> t) {
        super(t);
    }

    @Override
    public void serialize(XMLGregorianCalendar value, JsonGenerator gen, SerializerProvider provider)
        throws IOException
    {
        gen.writeNumber(value.toGregorianCalendar().getTimeInMillis());
    }

    @Override
    public void serializeWithType(XMLGregorianCalendar value, JsonGenerator gen, SerializerProvider provider,
        TypeSerializer typeSerializer) throws IOException
    {
        gen.writeStartArray();
        gen.writeString("javax.xml.datatype.XMLGregorianCalendar");
        serialize(value, gen, provider); // call your customized serialize method
        gen.writeEndArray();
    }
}

You can add this serializer to the object mapper with the code below. It can be pasted in the code example in the question.

    SimpleModule module = new SimpleModule();
    module.addSerializer(XMLGregorianCalendar.class, new XMLGregorianCalendarSerializer());
    mapper.registerModule(module);
Bouke Nijhuis
  • 349
  • 2
  • 7
  • Hi! It's work, yes, but it is a hack Maybe you know a better answer? – aarexer Nov 12 '19 at 13:37
  • Hi aarexer, I do not know of a better solution. Furthermore I do not see this as a hack. In my opinion, this is a proper solution. – Bouke Nijhuis Nov 13 '19 at 14:40
  • Why i think it's a hack - cause in every project which i want use XMLGregorianCalendar i need copy this code and register my own simple module. And it's not convenient as for me – aarexer Nov 14 '19 at 08:21
  • A related problem is discussed here: https://github.com/FasterXML/jackson-databind/issues/1791 And this one is tagged for version 2.11. So maybe it will be fixed in 2.11. – Bouke Nijhuis Nov 15 '19 at 09:45
  • seems it will be fixed in 2.11, yes Thanx – aarexer Nov 18 '19 at 12:28
  • #1791 is a different issue. This issue is fixed in 2.13: https://github.com/FasterXML/jackson-databind/issues/3217 – michaelok Aug 17 '21 at 13:22
0

XMLGregorian calendar suppose to handle serialization and deserialization alone. Therefore I prefer to use the following serializer and deserializer:

private class XMLCalendarDeserializer extends StdDeserializer<XMLGregorianCalendar> {

        private DatatypeFactory factory = DatatypeFactory.newDefaultInstance();
        
        public XMLCalendarDeserializer() {
            super(XMLGregorianCalendar.class);
        }

        
        
        @Override
        public XMLGregorianCalendar deserialize(JsonParser parser, DeserializationContext ctx) throws IOException, JsonProcessingException {
            if (parser.hasToken(JsonToken.VALUE_STRING)) {
                return factory.newXMLGregorianCalendar(parser.getText());
            } else {
                throw new JsonParseException(parser, "not string token");
            }
        }
        
    }
    
    private class XMLCalendarSerializer extends StdSerializer<XMLGregorianCalendar> {

        public XMLCalendarSerializer() {
            super(XMLGregorianCalendar.class);
        }
        
        @Override
        public void serialize(XMLGregorianCalendar val, JsonGenerator gen, SerializerProvider ser) throws IOException {
            gen.writeString(val.toXMLFormat());
        }
        
    }

`

Tamas
  • 81
  • 7