org.springframework.hateoas.hal.Jackson2HalModule.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.hateoas.hal.Jackson2HalModule.java

Source

/*
 * Copyright 2012-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.hateoas.hal;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.BeanUtils;
import org.springframework.hateoas.ImageName;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.MediaType;
import org.springframework.hateoas.RelProvider;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.core.JsonRelAsArray;
import org.springframework.hateoas.core.JsonResourceAsArray;
import org.springframework.util.Assert;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.ContainerSerializer;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.MapSerializer;
import com.fasterxml.jackson.databind.ser.std.NonTypedScalarSerializerBase;
import com.fasterxml.jackson.databind.type.TypeFactory;

/**
 * Jackson 2 module implementation to render {@link Link} and
 * {@link ResourceSupport} instances in HAL compatible JSON.
 *
 * @author Alexander Baetz
 * @author Oliver Gierke
 */
public class Jackson2HalModule extends SimpleModule {

    private static final long serialVersionUID = 7806951456457932384L;

    public Jackson2HalModule() {

        super("c4-ext-json-hal-module",
                new Version(1, 0, 0, null, "org.springframework.hateoas", "spring-hateoas"));

        setMixInAnnotation(Link.class, LinkMixin.class);
        setMixInAnnotation(ResourceSupport.class, ResourceSupportMixin.class);
        setMixInAnnotation(Resources.class, ResourcesMixin.class);
    }

    /**
     * Returns whether the module was already registered in the given
     * {@link ObjectMapper}.
     *
     * @param mapper
     *            must not be {@literal null}.
     * @return
     */
    public static boolean isAlreadyRegisteredIn(ObjectMapper mapper) {

        Assert.notNull(mapper, "ObjectMapper must not be null!");
        return LinkMixin.class.equals(mapper.findMixInClassFor(Link.class));
    }

    /**
     * Custom {@link JsonSerializer} to render Link instances in HAL compatible
     * JSON.
     *
     * @author Alexander Baetz
     * @author Oliver Gierke
     */
    public static class HalLinkListSerializer extends ContainerSerializer<List<Link>>
            implements ContextualSerializer {

        private final BeanProperty property;

        private final CurieProvider curieProvider;

        public HalLinkListSerializer(CurieProvider curieProvider) {
            this(null, curieProvider);
        }

        public HalLinkListSerializer(BeanProperty property, CurieProvider curieProvider) {

            super(List.class, false);
            this.property = property;
            this.curieProvider = curieProvider;
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java
         * .lang.Object, com.fasterxml.jackson.core.JsonGenerator,
         * com.fasterxml.jackson.databind.SerializerProvider)
         */
        @Override
        public void serialize(List<Link> value, JsonGenerator jgen, SerializerProvider provider)
                throws IOException, JsonGenerationException {

            // sort links according to their relation
            Map<String, List<Object>> sortedLinks = new LinkedHashMap<String, List<Object>>();
            List<Link> links = new ArrayList<Link>();

            boolean prefixingRequired = curieProvider != null;
            boolean curiedLinkPresent = false;

            for (Link link : value) {

                String rel = prefixingRequired ? curieProvider.getNamespacedRelFrom(link) : link.getRel();

                if (!link.getRel().equals(rel)) {
                    curiedLinkPresent = true;
                }

                if (sortedLinks.get(rel) == null) {
                    sortedLinks.put(rel, new ArrayList<Object>());
                }

                links.add(link);
                sortedLinks.get(rel).add(link);
            }

            if (prefixingRequired && curiedLinkPresent) {

                ArrayList<Object> curies = new ArrayList<Object>();
                curies.add(curieProvider.getCurieInformation(new Links(links)));

                sortedLinks.put("curies", curies);
            }

            TypeFactory typeFactory = provider.getConfig().getTypeFactory();
            JavaType keyType = typeFactory.uncheckedSimpleType(String.class);
            JavaType valueType = typeFactory.constructCollectionType(ArrayList.class, Object.class);
            JavaType mapType = typeFactory.constructMapType(HashMap.class, keyType, valueType);

            MapSerializer serializer = MapSerializer.construct(new String[] {}, mapType, true, null,
                    provider.findKeySerializer(keyType, null), new OptionalListJackson2Serializer(property), null);

            serializer.serialize(sortedLinks, jgen, provider);
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual
         * (com.fasterxml.jackson.databind.SerializerProvider,
         * com.fasterxml.jackson.databind.BeanProperty)
         */
        @Override
        public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property)
                throws JsonMappingException {
            return new HalLinkListSerializer(property, curieProvider);
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentType
         * ()
         */
        @Override
        public JavaType getContentType() {
            return null;
        }

        /*
         * (non-Javadoc)
         *
         * @see com.fasterxml.jackson.databind.ser.ContainerSerializer#
         * getContentSerializer()
         */
        @Override
        public JsonSerializer<?> getContentSerializer() {
            return null;
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.ser.ContainerSerializer#isEmpty(java
         * .lang.Object)
         */
        @Override
        public boolean isEmpty(List<Link> value) {
            return value.isEmpty();
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement
         * (java.lang.Object)
         */
        @Override
        public boolean hasSingleElement(List<Link> value) {
            return value.size() == 1;
        }

        /*
         * (non-Javadoc)
         *
         * @see com.fasterxml.jackson.databind.ser.ContainerSerializer#
         * _withValueTypeSerializer
         * (com.fasterxml.jackson.databind.jsontype.TypeSerializer)
         */
        @Override
        protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
            return null;
        }
    }

    /**
     * Custom {@link JsonSerializer} to render Link instances in HAL compatible
     * JSON. Renders the {@link Link} as immediate object if we have a single
     * one or as array if we have multiple ones.
     *
     * @author Alexander Baetz
     * @author Oliver Gierke
     */
    public static class OptionalListJackson2Serializer extends ContainerSerializer<Object>
            implements ContextualSerializer {

        private final BeanProperty property;

        private final Map<Class<?>, JsonSerializer<Object>> serializers;

        public OptionalListJackson2Serializer() {
            this(null);
        }

        /**
         * Creates a new {@link OptionalListJackson2Serializer} using the given
         * {@link BeanProperty}.
         *
         * @param property
         */
        public OptionalListJackson2Serializer(BeanProperty property) {

            super(List.class, false);
            this.property = property;
            this.serializers = new HashMap<Class<?>, JsonSerializer<Object>>();
        }

        /*
         * (non-Javadoc)
         *
         * @see com.fasterxml.jackson.databind.ser.ContainerSerializer#
         * _withValueTypeSerializer
         * (com.fasterxml.jackson.databind.jsontype.TypeSerializer)
         */
        @Override
        public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
            throw new UnsupportedOperationException("not implemented");
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java
         * .lang.Object, com.fasterxml.jackson.core.JsonGenerator,
         * com.fasterxml.jackson.databind.SerializerProvider)
         */
        @Override
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
                throws IOException, JsonGenerationException {

            List<?> list = (List<?>) value;

            if (list.isEmpty()) {
                return;
            }

            // render as object if Link is not of type ArrayProfiledLink
            if (list.size() == 1) {
                if (!renderAsArray(list.get(0))) {

                    serializeContents(list.iterator(), jgen, provider);
                    return;
                }
            }

            jgen.writeStartArray();
            serializeContents(list.iterator(), jgen, provider);
            jgen.writeEndArray();
        }

        private boolean renderAsArray(Object object) {
            boolean renderAsAnArray = false;
            Class<?> clazz = object.getClass();
            if (clazz.isAnnotationPresent(JsonRelAsArray.class)) {
                if (object instanceof Link) {
                    String linkRelation = ((Link) object).getRel();
                    String[] relations = clazz.getAnnotation(JsonRelAsArray.class).value();
                    for (String relation : relations) {
                        if (relation.equals(linkRelation)) {
                            renderAsAnArray = true;
                            break;
                        }
                    }
                }
            }

            return renderAsAnArray;
        }

        private void serializeContents(Iterator<?> value, JsonGenerator jgen, SerializerProvider provider)
                throws IOException, JsonGenerationException {

            while (value.hasNext()) {
                Object elem = value.next();
                if (elem == null) {
                    provider.defaultSerializeNull(jgen);
                } else {
                    getOrLookupSerializerFor(elem.getClass(), provider).serialize(elem, jgen, provider);
                }
            }
        }

        private JsonSerializer<Object> getOrLookupSerializerFor(Class<?> type, SerializerProvider provider)
                throws JsonMappingException {

            JsonSerializer<Object> serializer = serializers.get(type);

            if (serializer == null) {
                serializer = provider.findValueSerializer(type, property);
                serializers.put(type, serializer);
            }

            return serializer;
        }

        /*
         * (non-Javadoc)
         *
         * @see com.fasterxml.jackson.databind.ser.ContainerSerializer#
         * getContentSerializer()
         */
        @Override
        public JsonSerializer<?> getContentSerializer() {
            return null;
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentType
         * ()
         */
        @Override
        public JavaType getContentType() {
            return null;
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.ser.ContainerSerializer#hasSingleElement
         * (java.lang.Object)
         */
        @Override
        public boolean hasSingleElement(Object arg0) {
            return false;
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.ser.ContainerSerializer#isEmpty(java
         * .lang.Object)
         */
        @Override
        public boolean isEmpty(Object arg0) {
            return false;
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual
         * (com.fasterxml.jackson.databind.SerializerProvider,
         * com.fasterxml.jackson.databind.BeanProperty)
         */
        @Override
        public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property)
                throws JsonMappingException {
            return new OptionalListJackson2Serializer(property);
        }
    }

    public static class HalLinkListDeserializer extends ContainerDeserializerBase<List<Link>> {

        private static final long serialVersionUID = 6420432361123210955L;

        @SuppressWarnings("deprecation")
        public HalLinkListDeserializer() {
            super(List.class);
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase
         * #getContentType()
         */
        @Override
        public JavaType getContentType() {
            return null;
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase
         * #getContentDeserializer()
         */
        @Override
        public JsonDeserializer<Object> getContentDeserializer() {
            return null;
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml
         * .jackson.core.JsonParser,
         * com.fasterxml.jackson.databind.DeserializationContext)
         */
        @Override
        public List<Link> deserialize(JsonParser jp, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {

            List<Link> result = new ArrayList<Link>();
            String relation;
            Link link;

            // links is an object, so we parse till we find its end.
            while (!JsonToken.END_OBJECT.equals(jp.nextToken())) {
                if (!JsonToken.FIELD_NAME.equals(jp.getCurrentToken())) {
                    throw new JsonParseException("Expected relation name", jp.getCurrentLocation());
                }

                // save the relation in case the link does not contain it
                relation = jp.getText();

                if (JsonToken.START_ARRAY.equals(jp.nextToken())) {
                    while (!JsonToken.END_ARRAY.equals(jp.nextToken())) {
                        link = jp.readValueAs(Link.class);
                        if (isImageLink(link)) {
                            String name = link.getName() != null ? link.getName() : ImageName.DEFAULT.getName();
                            String type = link.getType() != null ? link.getType()
                                    : MediaType.IMAGE_JPEG.getContentType();
                            result.add(new Link(link.getHref(), relation, name, link.getTitle(), type));
                        } else {
                            String profile = link.getProfile();
                            if (profile != null) {
                                result.add(new Link(link.getHref(), relation, profile, link.getName()));
                            } else {
                                result.add(new Link(link.getHref(), relation, link.getName(), link.getTitle(),
                                        link.getType()));
                            }
                        }
                    }
                } else {
                    link = jp.readValueAs(Link.class);
                    if (isImageLink(link)) {
                        String name = link.getName() != null ? link.getName() : ImageName.DEFAULT.getName();
                        String type = link.getType() != null ? link.getType()
                                : MediaType.IMAGE_JPEG.getContentType();
                        result.add(new Link(link.getHref(), relation, name, link.getTitle(), type));
                    } else {
                        String profile = link.getProfile();
                        if (profile != null) {
                            result.add(new Link(link.getHref(), relation, profile, link.getName()));
                        } else {
                            result.add(new Link(link.getHref(), relation, link.getName(), link.getTitle(),
                                    link.getType()));
                        }
                    }
                }
            }

            return result;
        }

        private boolean isImageLink(Link link) {
            if (link.getTitle() != null) {
                return true;
            }
            return false;
        }
    }

    /**
     * Custom {@link JsonSerializer} to render
     * {@link org.springframework.hateoas.Resource}-Lists in HAL compatible
     * JSON. Renders the list as a Map.
     *
     * @author Alexander Baetz
     * @author Oliver Gierke
     */
    public static class HalResourcesSerializer extends ContainerSerializer<Collection<?>>
            implements ContextualSerializer {

        private final BeanProperty property;

        private final RelProvider relProvider;

        private final boolean enforceEmbeddedCollections;

        public HalResourcesSerializer(RelProvider relPorvider, boolean enforceEmbeddedCollections) {
            this(null, relPorvider, enforceEmbeddedCollections);
        }

        public HalResourcesSerializer(BeanProperty property, RelProvider relProvider,
                boolean enforceEmbeddedCollections) {

            super(Collection.class, false);

            this.property = property;
            this.relProvider = relProvider;
            this.enforceEmbeddedCollections = enforceEmbeddedCollections;
        }

        @Override
        public void serialize(Collection<?> value, JsonGenerator jgen, SerializerProvider provider)
                throws IOException, JsonGenerationException {

            boolean overriddenEnforceEmbeddedCollections = enforceEmbeddedCollections;
            if (!value.isEmpty()) {
                Object content = value.iterator().next();

                overriddenEnforceEmbeddedCollections = content.getClass()
                        .isAnnotationPresent(JsonResourceAsArray.class);
            }

            HalEmbeddedBuilder builder = new HalEmbeddedBuilder(relProvider, overriddenEnforceEmbeddedCollections);

            for (Object resource : value) {
                builder.add(resource);
            }

            provider.findValueSerializer(Map.class, property).serialize(builder.asMap(), jgen, provider);
        }

        @Override
        public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
                throws JsonMappingException {
            return new HalResourcesSerializer(property, relProvider, enforceEmbeddedCollections);
        }

        @Override
        public JavaType getContentType() {
            return null;
        }

        @Override
        public JsonSerializer<?> getContentSerializer() {
            return null;
        }

        @Override
        public boolean isEmpty(Collection<?> value) {
            return value.isEmpty();
        }

        @Override
        public boolean hasSingleElement(Collection<?> value) {
            return value.size() == 1;
        }

        @Override
        protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
            return null;
        }
    }

    public static class HalResourcesDeserializer extends ContainerDeserializerBase<List<Object>>
            implements ContextualDeserializer {

        private static final long serialVersionUID = 4755806754621032622L;

        private final JavaType contentType;

        public HalResourcesDeserializer() {
            this(List.class, null);
        }

        public HalResourcesDeserializer(JavaType vc) {
            this(null, vc);
        }

        @SuppressWarnings("deprecation")
        private HalResourcesDeserializer(Class<?> type, JavaType contentType) {

            super(type);
            this.contentType = contentType;
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase
         * #getContentType()
         */
        @Override
        public JavaType getContentType() {
            return null;
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase
         * #getContentDeserializer()
         */
        @Override
        public JsonDeserializer<Object> getContentDeserializer() {
            return null;
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml
         * .jackson.core.JsonParser,
         * com.fasterxml.jackson.databind.DeserializationContext)
         */
        @Override
        public List<Object> deserialize(JsonParser jp, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {

            List<Object> result = new ArrayList<Object>();
            JsonDeserializer<Object> deser = ctxt.findRootValueDeserializer(contentType);
            Object object;

            // links is an object, so we parse till we find its end.
            while (!JsonToken.END_OBJECT.equals(jp.nextToken())) {
                if (!JsonToken.FIELD_NAME.equals(jp.getCurrentToken())) {
                    throw new JsonParseException("Expected relation name", jp.getCurrentLocation());
                }

                if (JsonToken.START_ARRAY.equals(jp.nextToken())) {
                    while (!JsonToken.END_ARRAY.equals(jp.nextToken())) {
                        object = deser.deserialize(jp, ctxt);
                        ;
                        result.add(object);
                    }
                } else {
                    object = deser.deserialize(jp, ctxt);
                    result.add(object);
                }
            }

            return result;
        }

        @Override
        public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
                throws JsonMappingException {

            JavaType vc = property.getType().getContentType();
            HalResourcesDeserializer des = new HalResourcesDeserializer(vc);
            return des;
        }
    }

    public static class HalHandlerInstantiator extends HandlerInstantiator {

        private final Map<Class<?>, Object> instanceMap = new HashMap<Class<?>, Object>();

        public HalHandlerInstantiator(RelProvider resolver, CurieProvider curieProvider) {
            this(resolver, curieProvider, true);
        }

        public HalHandlerInstantiator(RelProvider resolver, CurieProvider curieProvider,
                boolean enforceEmbeddedCollections) {

            Assert.notNull(resolver, "RelProvider must not be null!");

            HalResourcesSerializer resourcesSerializer = new HalResourcesSerializer(resolver,
                    enforceEmbeddedCollections);
            HalLinkListSerializer linkListSerializer = new HalLinkListSerializer(curieProvider);

            // customised
            this.instanceMap.put(HalResourcesSerializer.class, resourcesSerializer);
            this.instanceMap.put(HalLinkListSerializer.class, linkListSerializer);

            // overriden
            this.instanceMap.put(org.springframework.hateoas.hal.Jackson2HalModule.HalLinkListSerializer.class,
                    linkListSerializer);
            this.instanceMap.put(org.springframework.hateoas.hal.Jackson2HalModule.HalResourcesSerializer.class,
                    resourcesSerializer);
        }

        private Object findInstance(Class<?> type) {

            Object result = instanceMap.get(type);
            return result != null ? result : BeanUtils.instantiateClass(type);
        }

        /*
         * (non-Javadoc)
         *
         * @see com.fasterxml.jackson.databind.cfg.HandlerInstantiator#
         * deserializerInstance
         * (com.fasterxml.jackson.databind.DeserializationConfig,
         * com.fasterxml.jackson.databind.introspect.Annotated, java.lang.Class)
         */
        @Override
        public JsonDeserializer<?> deserializerInstance(DeserializationConfig config, Annotated annotated,
                Class<?> deserClass) {
            return (JsonDeserializer<?>) findInstance(deserClass);
        }

        /*
         * (non-Javadoc)
         *
         * @see com.fasterxml.jackson.databind.cfg.HandlerInstantiator#
         * keyDeserializerInstance
         * (com.fasterxml.jackson.databind.DeserializationConfig,
         * com.fasterxml.jackson.databind.introspect.Annotated, java.lang.Class)
         */
        @Override
        public KeyDeserializer keyDeserializerInstance(DeserializationConfig config, Annotated annotated,
                Class<?> keyDeserClass) {
            return (KeyDeserializer) findInstance(keyDeserClass);
        }

        /*
         * (non-Javadoc)
         *
         * @see
         * com.fasterxml.jackson.databind.cfg.HandlerInstantiator#serializerInstance
         * (com.fasterxml.jackson.databind.SerializationConfig,
         * com.fasterxml.jackson.databind.introspect.Annotated, java.lang.Class)
         */
        @Override
        public JsonSerializer<?> serializerInstance(SerializationConfig config, Annotated annotated,
                Class<?> serClass) {
            return (JsonSerializer<?>) findInstance(serClass);
        }

        /*
         * (non-Javadoc)
         *
         * @see com.fasterxml.jackson.databind.cfg.HandlerInstantiator#
         * typeResolverBuilderInstance
         * (com.fasterxml.jackson.databind.cfg.MapperConfig,
         * com.fasterxml.jackson.databind.introspect.Annotated, java.lang.Class)
         */
        @Override
        public TypeResolverBuilder<?> typeResolverBuilderInstance(MapperConfig<?> config, Annotated annotated,
                Class<?> builderClass) {
            return (TypeResolverBuilder<?>) findInstance(builderClass);
        }

        /*
         * (non-Javadoc)
         *
         * @see com.fasterxml.jackson.databind.cfg.HandlerInstantiator#
         * typeIdResolverInstance
         * (com.fasterxml.jackson.databind.cfg.MapperConfig,
         * com.fasterxml.jackson.databind.introspect.Annotated, java.lang.Class)
         */
        @Override
        public TypeIdResolver typeIdResolverInstance(MapperConfig<?> config, Annotated annotated,
                Class<?> resolverClass) {
            return (TypeIdResolver) findInstance(resolverClass);
        }
    }

    /**
     * {@link JsonSerializer} to only render {@link Boolean} values if they're
     * set to {@literal true}.
     *
     * @author Oliver Gierke
     * @since 0.9
     */
    public static class TrueOnlyBooleanSerializer extends NonTypedScalarSerializerBase<Boolean> {

        public TrueOnlyBooleanSerializer() {
            super(Boolean.class);
        }

        @Override
        public boolean isEmpty(Boolean value) {
            return (value == null) || Boolean.FALSE.equals(value);
        }

        @Override
        public void serialize(Boolean value, JsonGenerator jgen, SerializerProvider provider)
                throws IOException, JsonGenerationException {
            jgen.writeBoolean(value.booleanValue());
        }

        @Override
        public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
            return createSchemaNode("boolean", true);
        }

        @Override
        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
                throws JsonMappingException {
            if (visitor != null) {
                visitor.expectBooleanFormat(typeHint);
            }
        }
    }
}