7

With Jackson, it's easy to disable all annotations for a given ObjectMapper. Is there a way to only disable one given annotation?

// disable all
ObjectMapper mapper = new ObjectMapper()
mapper.disable(MapperFeature.USE_ANNOTATIONS);

// disable one?
ObjectMapper mapper = new ObjectMapper()
mapper.disable(@JsonIgnore);

Using @JacksonAnnotationsInside, I've defined a custom Jackson annotation and I only want it to be used in certain circumstances.

Srinivas
  • 1,780
  • 1
  • 14
  • 27
yves amsellem
  • 7,106
  • 5
  • 44
  • 68
  • There does not seem the way using this disable call but what does your custom annotation do? there may be another way to achieve the desired behavior. – Usman Ismail Feb 27 '13 at 20:22
  • The annotation add a custom de/serializer that is not wanted for this mapper. – yves amsellem Mar 11 '13 at 10:49
  • Would jackson views not suffice? If all you are doing in the custom de/serializer is filtering which fields are read or written then views can do the same thing. See here (http://www.techtraits.com/Programming/2011/08/12/implementing-jackson-views/) – Usman Ismail Mar 11 '13 at 16:01
  • The de/serializer transforms the String into MongoDB ObjectId. This question is related to [this github post](https://github.com/FasterXML/jackson-databind/issues/168#issuecomment-13695863). – yves amsellem Mar 11 '13 at 16:09
  • Hmm this may be a longshot but, with jackson any getter setter method can be annotated with JsonProperty. So if you put a JsonIgnore on the string member variable and then put JsonProperty on a getMongoID() method. Then within the code of this method you can do the conversion to MongoID. Similarly do the reverse conversion in the setMongoID(MongoID id) method. – Usman Ismail Mar 11 '13 at 16:22
  • Are you suggesting to extract the de/serializer into the object methods? Because this means duplicating its code on every object that need this... – yves amsellem Mar 11 '13 at 16:58
  • I was suggesting that, you could use inheritance to move this getter into a parent class. That is if all the classes that need this logic are logically such that they can be in one hierarchy. – Usman Ismail Mar 11 '13 at 17:48

4 Answers4

2

This the best I've come across. I think I saw it on the Jackson user group forums somewhere.

Essentially it makes a custom annotation introspector, which returns null if it sees that it has a specific annotation (in this case JsonTypeInfo)

JacksonAnnotationIntrospector ignoreJsonTypeInfoIntrospector = new JacksonAnnotationIntrospector() {
            @Override
            protected TypeResolverBuilder<?> _findTypeResolver(
                    MapperConfig<?> config, Annotated ann, JavaType baseType) {
                if (!ann.hasAnnotation(JsonTypeInfo.class)) {
                    return super._findTypeResolver(config, ann, baseType);
                }
                return null;
            }
        };

        mapper.setAnnotationIntrospector(ignoreJsonTypeInfoIntrospector);
755
  • 2,991
  • 3
  • 20
  • 34
  • 2
    One important note on this solution: rather than returning null, you should return StdTypeResolverBuilder.noTypeInfoBuilder(). In my case, I was using Jackson for my JSON processor in Jersey. When certain Jersey Features (e.g., EntityFiltering) was used, the processing workflow of serializing to JSON doesn't act as expected during the annotation introspector phase. In my specific case, when findTypeResovler returned null, Jersey Filtering used its "secondary" annotation introspector to successfully serialize JsonTypeInfo – ChrisO Dec 15 '17 at 22:13
  • This was with Jackson 2.8.9 – ChrisO Dec 15 '17 at 22:20
0

I think it's a better idea to override findPropertiesToIgnore method like this:

    JacksonAnnotationIntrospector ignoreJsonTypeInfoIntrospector = new JacksonAnnotationIntrospector() {
        @Override
        public String[] findPropertiesToIgnore(AnnotatedClass ac) {
            ArrayList<String> ret = new ArrayList<String>();
            for (Method m : ac.getRawType().getMethods()) {
                if(ReflectionUtils.isGetter(m)){
                    if(m.getAnnotation(Transient.class) != null)
                        ret.add(ReflectionUtils.getPropertyName(m));

                }
            };
            return ret.toArray(new String[]{});
        }
    };
    objectMapper = new ObjectMapper();
    objectMapper.setAnnotationIntrospector(ignoreJsonTypeInfoIntrospector);
hasan
  • 966
  • 2
  • 13
  • 40
0

This solution worked for me. Check this for more info

 private static final JacksonAnnotationIntrospector IGNORE_ENUM_ALIAS_ANNOTATIONS = new JacksonAnnotationIntrospector() {

    @Override
    protected <A extends Annotation> A _findAnnotation(final Annotated annotated, final Class<A> annoClass) {
        if (!annotated.hasAnnotation(JsonEnumAliasSerializer.class)) {
            return super._findAnnotation(annotated, annoClass);
        }
        return null;
    }
};

And my custom annotation:

@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = JsonEnumSerializer.class)
public @interface JsonEnumAliasSerializer {
}

And ObjectMapper:

    final ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setAnnotationIntrospector(IGNORE_ENUM_ALIAS_ANNOTATIONS);
Oleg Kuts
  • 769
  • 1
  • 13
  • 26
0

Here is a simpler way (August 2022) to do it in Kotlin (you can translate to Java if you really want to):

(I use this in a Ktor/KMongo project to prevent Transient attributes from being persisted because KMongo requires @JsonIgnore, which then also prevents my other mappers from serializing those properties!)

@Target(PROPERTY_GETTER)
annotation class Transient

private val TransientIntrospector: JacksonAnnotationIntrospector = object : JacksonAnnotationIntrospector() {
    override fun hasIgnoreMarker(m: AnnotatedMember): Boolean =
        m.allAnnotations.has(Transient::class.java) || super.hasIgnoreMarker(m)
}

private class IgnoreTransientModule : SimpleModule() {
    override fun setupModule(context: SetupContext) {
        super.setupModule(context)
        context.appendAnnotationIntrospector(TransientIntrospector)
    }
}

// Just register the module in your `ObjectMapper` instance:
val ignoreTransientMapper = with(ObjectMapper()) {
    registerModule(IgnoreTransientModule())
}

// And here is how to use it:
data class Customer(
    val id: String? = null,
    val firstName: String,
    val lastName: String,
) {
    @get:Transient
    val fullName: String
        get() = "$firstName $lastName"
}

Tom Grushka
  • 439
  • 5
  • 9