KIC
KIC

Reputation: 6121

Java binary serializing fails because of jackson

I use jackson 2 to convert json into a java object. So far so good. But I also use hazelcast to distribute the objects in a cluster. Therefore all beans have to be java.io.Serializable. When I read the Object from json like so:

ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(AbstractBean.class, MongoIdMixIn.class);

// this is to prevent from failing on missing type class property: @JsonProperty("@class")
Object tgtObject = targetClass.newInstance();
mapper.readerForUpdating(tgtObject).readValue(dbo.toString());

// put into hazelcast map
target.put(dbo.get(keyColumn), tgtObject); 

I will get an exception from hazelcast:

java.io.NotSerializableException: com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer

I am wondering where the com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer is coming from since the Object is a plain java bean (but using inheritance).

My Abstract class is:

@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@javaClass")
public abstract class AbstractBean implements Serializable {
    @JsonIgnore public static final transient IMarkupParser MARKUP_PARSER = new WikiMarkupParser();

    @JsonProperty("id")
    private String id;

    @JsonProperty("@class")
    private String clazz;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getClazz() {
        return this.getClass().getSimpleName();
    }
}

And my child is:

public class Posting extends AbstractBean {
    private String postingSource;
    private String languageCode;

    public String getPostingSource() {
        return postingSource;
    }

    public void setPostingSource(String postingSource) {
        this.postingSource = postingSource;
    }

    public String getLanguageCode() {
        return languageCode;
    }

    public void setLanguageCode(String languageCode) {
        this.languageCode = languageCode;
    }
}

I have no Idea why the serailizer would even try to serialize the mixins since the are not part of the bean but here they are (yes I have tried to make them serializable too, just as a test, no luck):

public interface IdMixins extends Serializable {
}

public interface MongoIdMixIn extends IdMixins {
    @JsonProperty("_id")
    @JsonSerialize(using = MongoIdSerializer.class)
    public String getId();

    @JsonProperty("_id")
    @JsonDeserialize(using = MongoIdDeserializer.class)
    public void setId(String id);
}

public class MongoIdDeserializer extends JsonDeserializer<String> implements Serializable {
    private static final long serialVersionUID = -5404276857799190647L;

    @Override
    public String deserialize(JsonParser jp, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        String value = null;

        String tmp = jp.getText(); // {
        validate(jp, tmp,"{");
        int curly = 1;

        while (jp.nextToken() != null) {
            String v = jp.getText();

            if (v.equals("{")) curly++;

            if (v.equals("$oid")) {
                jp.nextToken();
                value = jp.getText();
            }

            if (v.equals("}")) curly--;
            if (curly<=0) return value;
        }

        return null;
    }

    private void validate(JsonParser jsonParser, String input, String expected) throws JsonProcessingException {
        if (!input.equals(expected)) {
            throw new JsonParseException("Unexpected token: " + input, jsonParser.getTokenLocation());
        }
    }
}

public class MongoIdSerializer extends JsonSerializer<String> implements Serializable {
    private static final long serialVersionUID = 3435689991839324194L;

    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeFieldName("$oid");
        jsonGenerator.writeString(s);
        jsonGenerator.writeEndObject();
    }
}

Upvotes: 4

Views: 1695

Answers (2)

KIC
KIC

Reputation: 6121

Stupid me! Somewhere in the serialization chain was a completely unnecessary ObjectMapper object. But it was hard to find because not the Posting object was the real reason, instead it was another object. But the Stacktrace and the com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer Exception were completely miss leading! ... clustered software is sometimes really painful to debug :-)

Upvotes: 1

Peter Paul Kiefer
Peter Paul Kiefer

Reputation: 2124

I'm 1 Rep. Point away from being able to comment. So I have to make a suggestion as an answer ;-).

Perhaps one of the Annotations do inject an instance of TypeWrappedDeserializer as a private property into the AbstractBean. Maybe as hint for the deserialization mechanism.

Could you inspect the created object with reflection to verify?

for (Field field : tgtObject.getClass().getDeclaredFields() )
{
   // you can replace this by your logging method
   System.out.println("Field: " + field.getName() + ":" + field.getType());
}
for (Field field : tgtObject.getClass().getSuperclass().getDeclaredFields() )
{
   // you can replace this by your logging method
   System.out.println("Field: " + field.getName() + ":" + field.getType());
}

If you find the apropriate type in the listing the Class was added by Byte Code Enhancement.

Upvotes: 0

Related Questions