Missing FormatMapper for JSON format with Jackson 3.x, Hibernate 7.x

Hello,

I’m encountering the following issue:
Could not find a FormatMapper for the JSON format, which is required for mapping JSON types. JSON FormatMapper configuration is automatic, but requires that you have either Jackson or a JSONB implementation like Yasson on the class path
In Jackson 3.x, the package com.fasterxml.jackson was renamed to tools.jackson. This change seems to break the auto-configuration of the FormatMapper for JSON fields
I tried manually defining a FormatMapper bean, but that fails because JsonProcessingException (and other core Jackson types) are no longer in the expected location, causing classpath or compatibility issues.

I know I could downgrade to Jackson 2.x as a workaround, but I’d prefer to keep using Jackson 3.x if possible. Is there a way to make this work with the current stack?

My current dependencies:

Spring Boot starter: 4.0.0-SNAPSHOT

Jackson Databind: 3.0.0-rc9

Hibernate Core: 7.1.1.Final

1 Like

You can write your own format mapper using Jackson 3 and tell Hibernate ORM to use it: Hibernate ORM User Guide

It’s really not a complicated interface, see the one for Jackson 2: hibernate-orm/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java at ae2e4d9a527121084bb65ab04e3d3e3af3c20e44 · hibernate/hibernate-orm · GitHub

For Spring Boot (we use 4.0.1), don’t forget to reference your custom format mapper in the application properties like this :

spring.jpa.properties.hibernate.type.json_format_mapper=com.yourcorp.yourapp.utilities.CustomHibernateJsonFormatter

Here is our implementation (just took the model and changed to tools.jackson) :

package com.yourcorp.yourapp.utilities;

import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.format.AbstractJsonFormatMapper;
import tools.jackson.core.JsonGenerator;
import tools.jackson.core.JsonParser;
import tools.jackson.databind.json.JsonMapper;


import java.lang.reflect.Type;

/**
 *
 * @author Christian Beikov
 * @author Yanming Zhou
 */
public final class CustomHibernateJsonFormatter extends AbstractJsonFormatMapper {

    public static final String SHORT_NAME = "jackson";

    private final JsonMapper jsonMapper;

    public CustomHibernateJsonFormatter() {
        this(JsonMapper.builder().build());
    }

    public CustomHibernateJsonFormatter(JsonMapper jsonMapper) {
        this.jsonMapper = jsonMapper;
    }

    @Override
    public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options) {
        jsonMapper.writerFor( jsonMapper.constructType( javaType.getJavaType() ) )
                .writeValue( (JsonGenerator) target, value );
    }

    @Override
    public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) {
        return jsonMapper.readValue( (JsonParser) source, jsonMapper.constructType( javaType.getJavaType() ) );
    }

    @Override
    public boolean supportsSourceType(Class<?> sourceType) {
        return JsonParser.class.isAssignableFrom( sourceType );
    }

    @Override
    public boolean supportsTargetType(Class<?> targetType) {
        return JsonGenerator.class.isAssignableFrom( targetType );
    }

    @Override
    public <T> T fromString(CharSequence charSequence, Type type) {
        return jsonMapper.readValue( charSequence.toString(), jsonMapper.constructType( type ) );
    }

    @Override
    public <T> String toString(T value, Type type) {
        return jsonMapper.writerFor( jsonMapper.constructType( type ) ).writeValueAsString( value );
    }
}