Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to declare another Jackson ObjectMapper without affecting 'clients' of the original bean?

I have a spring-boot application that exposes a json REST API. For mapping objects to json it uses the built-in jackson ObjectMapper configured by spring-boot.

Now I need to read some data from a yaml file and I found that an easy way to do it is using Jackson - for this I need to declare a different ObjectMapper to convert yaml to objects. I declared this new mapper bean with a specific name to be able to inject it in my service dealing with reading from the yaml file:

@Bean(YAML_OBJECT_MAPPER_BEAN_ID)
public ObjectMapper yamlObjectMapper() {
    return new ObjectMapper(new YAMLFactory());
}

But I need a way to tell all the other "clients" of the original json ObjectMapper to keep using that bean. So basically I would need a @Primary annotation on the original bean. Is there a way to achieve this without having to redeclare the original ObjectMapper in my own configuration (I'd have to dig through spring-boot code to find and copy its configuration)?

One solution I found is to declare a FactoryBean for ObjectMapper and make it return the already declared bean, as suggested in this answer. I found by debugging that my original bean is called "_halObjectMapper", so my factoryBean will search for this bean and return it:

public class ObjectMapperFactory implements FactoryBean<ObjectMapper> {

    ListableBeanFactory beanFactory;

    public ObjectMapper getObject() {
        return beanFactory.getBean("_halObjectMapper", ObjectMapper.class);
    }
    ...
}

Then in my Configuration class I declare it as a @Primary bean to make sure it's the first choice for autowiring:

@Primary
@Bean
public ObjectMapperFactory objectMapperFactory(ListableBeanFactory beanFactory) {
    return new ObjectMapperFactory(beanFactory);
}

Still, I'm not 100% happy with this solution because it relies on the name of the bean which is not under my control, and it also seems like a hack. Is there a cleaner solution?

Thanks!

like image 395
Timi Avatar asked May 26 '17 06:05

Timi


1 Answers

Other option is to wrap custom mapper into custom object:

@Component
public class YamlObjectMapper {
    private final ObjectMapper objectMapper;

    public YamlObjectMapper() {
        objectMapper = new ObjectMapper(new YAMLFactory());
    }

    public ObjectMapper getMapper() {
        return objectMapper;
    }
}

Unfortunately this approach requires calling getMapper after you inject YamlObjectMapper.

like image 134
luboskrnac Avatar answered Oct 03 '22 22:10

luboskrnac