@Override
    public ObjectMapper createMapper() {
        final ObjectMapper om = new ObjectMapper(JsonFactory.builder().streamReadConstraints(streamReadConstraints).build());
        if (version != GraphSONVersion.V4_0) {
            om.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        }

        final GraphSONModule graphSONModule = version.getBuilder().create(normalize, typeInfo);
        om.registerModule(graphSONModule);
        customModules.forEach(om::registerModule);

        // plugin external serialization modules
        if (loadCustomSerializers)
            om.findAndRegisterModules();

        if ((version == GraphSONVersion.V4_0 || version == GraphSONVersion.V3_0 || version == GraphSONVersion.V2_0) &&
                typeInfo != TypeInfo.NO_TYPES) {
            final GraphSONTypeIdResolver graphSONTypeIdResolver = new GraphSONTypeIdResolver();
            final TypeResolverBuilder typer = new GraphSONTypeResolverBuilder(version)
                    .typesEmbedding(this.typeInfo)
                    .valuePropertyName(GraphSONTokens.VALUEPROP)
                    .init(JsonTypeInfo.Id.CUSTOM, graphSONTypeIdResolver)
                    .typeProperty(GraphSONTokens.VALUETYPE);

            // Registers native Java types that are supported by Jackson
            registerJavaBaseTypes(graphSONTypeIdResolver);

            // Registers the GraphSON Module's types
            graphSONModule.getTypeDefinitions().forEach(
                    (targetClass, typeId) -> graphSONTypeIdResolver.addCustomType(
                            String.format("%s:%s", graphSONModule.getTypeNamespace(), typeId), targetClass));

            // Register types to typeResolver for the Custom modules
            customModules.forEach(e -> {
                if (e instanceof TinkerPopJacksonModule) {
                    final TinkerPopJacksonModule mod = (TinkerPopJacksonModule) e;
                    final Map<Class, String> moduleTypeDefinitions = mod.getTypeDefinitions();
                    if (moduleTypeDefinitions != null) {
                        if (mod.getTypeNamespace() == null || mod.getTypeNamespace().isEmpty())
                            throw new IllegalStateException("Cannot specify a module for GraphSON 2.0 with type definitions but without a type Domain. " +
                                    "If no specific type domain is required, use Gremlin's default domain, \"gremlin\" but there may be collisions.");

                        moduleTypeDefinitions.forEach((targetClass, typeId) -> graphSONTypeIdResolver.addCustomType(
                                        String.format("%s:%s", mod.getTypeNamespace(), typeId), targetClass));
                    }
                }
            });
            om.setDefaultTyping(typer);
        } else if (version == GraphSONVersion.V1_0 || version == GraphSONVersion.V2_0) {
            if (typeInfo == TypeInfo.PARTIAL_TYPES) {
                final TypeResolverBuilder<?> typer = new StdTypeResolverBuilder()
                        .init(JsonTypeInfo.Id.CLASS, null)
                        .inclusion(JsonTypeInfo.As.PROPERTY)
                        .typeProperty(GraphSONTokens.CLASS);
                om.setDefaultTyping(typer);
            }
        } else if (version == GraphSONVersion.V3_0 || version == GraphSONVersion.V4_0) {

        } else {
            throw new IllegalStateException("Unknown GraphSONVersion: " + version);
        }

        // Starting with GraphSONv4, only types that can be returned from the result of a traversal are supported. This
        // differs to previous versions where a gremlin-groovy script could return any type. So if an unknown type is
        // encountered, an error should be thrown.
        if (version != GraphSONVersion.V4_0) {
            // this provider toStrings all unknown classes and converts keys in Map objects that are Object to String.
            final DefaultSerializerProvider provider = new GraphSONSerializerProvider(version);
            om.setSerializerProvider(provider);
        }

        if (normalize)
            om.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS);

        // keep streams open to accept multiple values (e.g. multiple vertices)
        om.getFactory().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
        return om;
    }
An extension to the standard Jackson {@code ObjectMapper} which automatically registers the standard
{@link GraphSONModule} for serializing {@link Graph} elements.  This class
can be used for generalized JSON serialization tasks that require meeting GraphSON standards.
<p/>
{@link Graph} implementations providing an {@link IoRegistry} should register their {@code SimpleModule}
implementations to it as follows:
<pre>
{@code
public class MyGraphIoRegistry extends AbstractIoRegistry {
  public MyGraphIoRegistry() {
    register(GraphSONIo.class, null, new MyGraphSimpleModule());
  }
}
}
</pre>

@author Stephen Mallette (http://stephen.genoprime.com)
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java