Jackson - ignore Map superclass when serializing
Asked Answered
K

4

5

I have a few model classes that extend LinkedHashMap<String, Object>: they define getters and setters which wrap the Map's get and put methods. I am trying to serialize instances of these classes using Jackson (with RESTEasy), but Jackson refuses to pay attention to my getters, which are annotated with @JsonProperty. Instead, it is only serializing the key-value pairs of the backing map. I tried using @JsonAutoDetect to disable auto-detection for all methods and fields, but that didn't change anything. Is there a way to prevent Jackson from automatically serializing a Map, or must I create new model classes that don't extend LinkedHashMap<String, Object>?

Keystroke answered 3/1, 2012 at 21:10 Comment(0)
M
7

I have a few model classes that extend LinkedHashMap<String, Object>: they define getters and setters which wrap the Map's get and put methods

This is a classic example of when not to use inheritance: you're finding that some other piece of code (i.e. Jackson) is treating your class like an instance of its superclass, which isn't what you want it to do. In cases like these (and also in general), it's usually better to use composition rather than inheritance.

I recommend rewriting your model class to contain a map, rather than extending one. You get much more control than way, and the resulting model is less brittle. If you need to view your model as a Map, then implement an asMap method (or something similar) which renders that view.

Meaganmeager answered 3/1, 2012 at 22:15 Comment(1)
Thanks for your response, skaffman. I was actually considering moving to a composition-based approach. LinkedHashMap is actually not a direct superclass of my model classes; instead, they extend MongoDB's BasicDBObject, which allows me to easily persist them. However, I may be trying to use these objects for too many purposes. Adding toDBObject() and fromDBObject() methods should work. However, I'm still curious as to whether or not there's a way to prevent Jackson from doing its default Map serialization. Is there a way to disable a serializer for a specific type?Keystroke
H
11

I agree with @skaffman's response. But if you could not easily change inheritance structure drastically, there may be ways around this.

One possibility is that if you do have an interface that defines getters/setters, you could add

@JsonSerialize(as=MyInterface.class)
@JsonDeserialize(as=MyInterface.class)

which would force Jackson to only use whatever is available via specific interface.

Custom serializers/deserializers are also a possibility, but that's quite a bit of work.

Haigh answered 4/1, 2012 at 17:39 Comment(0)
M
7

I have a few model classes that extend LinkedHashMap<String, Object>: they define getters and setters which wrap the Map's get and put methods

This is a classic example of when not to use inheritance: you're finding that some other piece of code (i.e. Jackson) is treating your class like an instance of its superclass, which isn't what you want it to do. In cases like these (and also in general), it's usually better to use composition rather than inheritance.

I recommend rewriting your model class to contain a map, rather than extending one. You get much more control than way, and the resulting model is less brittle. If you need to view your model as a Map, then implement an asMap method (or something similar) which renders that view.

Meaganmeager answered 3/1, 2012 at 22:15 Comment(1)
Thanks for your response, skaffman. I was actually considering moving to a composition-based approach. LinkedHashMap is actually not a direct superclass of my model classes; instead, they extend MongoDB's BasicDBObject, which allows me to easily persist them. However, I may be trying to use these objects for too many purposes. Adding toDBObject() and fromDBObject() methods should work. However, I'm still curious as to whether or not there's a way to prevent Jackson from doing its default Map serialization. Is there a way to disable a serializer for a specific type?Keystroke
K
2

You can implement your own org.codehaus.jackson.map.DeserializerProvider which extends Jackson's org.codehaus.jackson.map.deser.StdDeserializerProvider and overwrite method _createDeserializer:

import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.deser.StdDeserializerProvider;
import org.codehaus.jackson.map.DeserializationConfig;
...

class MyDeserializerProvider extends StdDeserializerProvider {

    @Override
    protected JsonDeserializer<Object> _createDeserializer(DeserializationConfig config, JavaType type, BeanProperty property) throws JsonMappingException {
        if (type.isMapLikeType()) {       // (1)
            return this._factory.createBeanDeserializer(config, this, type, property);
        } else {
            return super._createDeserializer(config, type, property);
        }
    }
}

(1) use if-condition that meets your needs

The custom deserializer is registered directly at the ObjectMapper:

ObjectMapper om = new ObjectMapper();
om.setDeserializerProvider(new MyDeserializerProvider());

I tested this with Jackson 1.9.11.

Keenakeenan answered 12/5, 2015 at 10:9 Comment(0)
C
0

In newer versions of jackson (>= 2.9, I guess) simply annotate your class with

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
Cesium answered 2/4, 2022 at 14:35 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.