Search code examples
javajsonjacksoncamunda

Jackson JsonDeserializer always gets null value


I am using Camunda workflow automatition and I would like to implement JSON serialization.

There is an example project here: https://github.com/camunda/camunda-bpm-examples/tree/master/spin/dataformat-configuration-global

I did everything accordingly, but Jackson cannot serialize the MxSwiftMessage object (which you can find here: https://github.com/prowide/prowide-iso20022/blob/develop/iso20022-core/src/main/java/com/prowidesoftware/swift/model/MxSwiftMessage.java) by default.

So I had to write a custom JsonSerializer and JsonDeserializer.

My problem is that in the Deserializer I always get a null string.

Here is the code:

MxSwiftMessageJsonSerializer

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.WritableTypeId;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.prowidesoftware.swift.model.MxSwiftMessage;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;

import static com.fasterxml.jackson.core.JsonToken.START_OBJECT;

public class MxSwiftMessageJsonSerializer extends StdSerializer<MxSwiftMessage> {

    private static final long serialVersionUID = 1L;

    public MxSwiftMessageJsonSerializer() {
        super(MxSwiftMessage.class);
    }

    @Override
    public void serializeWithType(MxSwiftMessage mxSwiftMessage, JsonGenerator gen,
                                  SerializerProvider provider, TypeSerializer typeSer)
            throws IOException, JsonProcessingException {
        WritableTypeId typeId = typeSer.typeId(mxSwiftMessage, START_OBJECT);
        typeSer.writeTypePrefix(gen, typeId);
        serialize(mxSwiftMessage, gen, provider); // call customized serialize method
        typeSer.writeTypeSuffix(gen, typeId);
    }

    @Override
    public void serialize(MxSwiftMessage mxSwiftMessage, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(mxSwiftMessage.getMessage());
    }
}

MxSwiftMessageJsonDeserializer

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.prowidesoftware.swift.model.MxSwiftMessage;

import java.io.IOException;

public class MxSwiftMessageJsonDeserializer extends JsonDeserializer<MxSwiftMessage> {

    @Override
    public MxSwiftMessage deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
        String xmlString = jsonParser.getValueAsString();
        return MxSwiftMessage.parse(xmlString);
    }
}

JacksonDataFormatConfigurator


import javax.xml.bind.JAXBElement;
import javax.xml.datatype.XMLGregorianCalendar;

import com.prowidesoftware.swift.model.MxSwiftMessage;
import org.camunda.spin.impl.json.jackson.format.JacksonJsonDataFormat;
import org.camunda.spin.spi.DataFormatConfigurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

public class JacksonDataFormatConfigurator implements DataFormatConfigurator<JacksonJsonDataFormat> {

    private static final Logger logger = LoggerFactory.getLogger(JacksonDataFormatConfigurator.class);

    @Override
    public Class<JacksonJsonDataFormat> getDataFormatClass() {
        return JacksonJsonDataFormat.class;
    }

    @Override
    public void configure(JacksonJsonDataFormat dataFormat) {
        logger.info("Object mapper configuration started");
        ObjectMapper objectMapper = dataFormat.getObjectMapper();
        configureObjectMapper(objectMapper);
        logger.info("Configuration finished");
    }

    public static void configureObjectMapper(ObjectMapper objectMapper) {
        SimpleModule mxSwiftMessageSerializer = new SimpleModule();
        mxSwiftMessageSerializer.addSerializer(MxSwiftMessage.class, new MxSwiftMessageJsonSerializer());
        mxSwiftMessageSerializer.addDeserializer(MxSwiftMessage.class, new MxSwiftMessageJsonDeserializer());
        objectMapper.registerModule(mxSwiftMessageSerializer);
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.registerModule(createXMLGregorianCalendarSerializerModule());
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
        objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
        objectMapper.addMixIn(JAXBElement.class, JAXBElementMixIn.class);
    }

    public static SimpleModule createXMLGregorianCalendarSerializerModule() {
        SimpleModule module = new SimpleModule();
        module.addSerializer(XMLGregorianCalendar.class, new XMLGregorianCalendarSerializer());
        return module;
    }

}

Dependency versions:

org.camunda.spin : 1.9.0

org.camunda.bpm : 7.12.0

com.fasterxml.jackson: 2.9.8

UPDATE:

The issue is highly connected with using Camunda. The problem arises only when entering into a call activity with the type of BPMN. I found this in the Camunda forum, which indicates I am not the only one who faced this error: https://forum.camunda.org/t/call-activity-deserialization-error/14911

Also there is a ticket for this (which is is closed status): https://jira.camunda.com/browse/CAM-3754

However I still did not find any solution for it.


Solution

  • I finally found a solution which works for me. The magic happens in the serializeWithType method. It has to write the data as an Array value.

    I also changed the underlying data structure to json object instead of the pure string to store the xml representation of the MxSwiftMessage object.

    This is the code:

    MxSwiftMessageJsonSerializer

    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
    import com.prowidesoftware.swift.model.MxSwiftMessage;
    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.SerializerProvider;
    import com.fasterxml.jackson.databind.ser.std.StdSerializer;
    import java.io.IOException;
    
    public class MxSwiftMessageJsonSerializer extends StdSerializer<MxSwiftMessage> {
    
        private static final long serialVersionUID = 1L;
    
        public MxSwiftMessageJsonSerializer() {
            super(MxSwiftMessage.class);
        }
    
        @Override
        public void serializeWithType(MxSwiftMessage mxSwiftMessage, JsonGenerator gen,
                                      SerializerProvider provider, TypeSerializer typeSer)
                throws IOException, JsonProcessingException {
            gen.writeStartArray();
            gen.writeString(MxSwiftMessage.class.getName());
            serialize(mxSwiftMessage, gen, provider);
            gen.writeEndArray();
        }
    
        @Override
        public void serialize(MxSwiftMessage mxSwiftMessage, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeStartObject();
            jsonGenerator.writeStringField("message", mxSwiftMessage.getMessage());
            jsonGenerator.writeEndObject();
        }
    }
    

    MxSwiftMessageJsonDeserializer

    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.JsonDeserializer;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.prowidesoftware.swift.model.MxSwiftMessage;
    
    import java.io.IOException;
    
    public class MxSwiftMessageJsonDeserializer extends JsonDeserializer<MxSwiftMessage> {
    
        @Override
        public MxSwiftMessage deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
            JsonNode node = jsonParser.getCodec().readTree(jsonParser);
            String xmlString = node.get("message").asText();
            return MxSwiftMessage.parse(xmlString);
        }
    }