Jackson ObjectMapper Config in Hippo CRISP

October 12th 2018 Hippo CMS Java Spring

The recommended way for integrating external services in Hippo CMS is the CRISP API. The official documentation provides detailed enough instructions for installing, configuring and using it. However, when I needed to customize the default Jackson ObjectMapper, it wasn't all that obvious how I could do it.

The REST service I had to call was returning a date value:

[ {
    "name": "John",
    "surname": "Doe",
    "birthDate": "1980-05-01"
} ]

In my Java code I wanted to deserialize it into a LocalDate:

public class Person {

    // ...
    private LocalDate birthDate;

    // ...

    public LocalDate getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;
    }
}

I configured a matching CRISP resource based on the example from the documentation, only removing the resourceLinkResolver.

The code for calling the service and deserializing the result is pretty straightforward:

ResourceServiceBroker broker =
    HippoServiceRegistry.getService(ResourceServiceBroker.class);
Resource resourceRoot = broker.resolve(DEMO_RESOURCE, "/persons");
ResourceCollection resourceCollection = resourceRoot.getChildren();
ResourceBeanMapper resourceBeanMapper = broker.getResourceBeanMapper(DEMO_RESOURCE);
Collection<Person> persons =
    resourceBeanMapper.mapCollection(resourceCollection, Person.class);

Unfortunately, the deserialization failed:

java.lang.IllegalArgumentException: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value ('1980-05-01')

To make it work, I would need to register the JavaTimeModule. I started by adding the dependency to the site module pom.xml:

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

And the root pom.xml:

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jsr310</artifactId>
  <version>${jackson.jsr310.version}</version>
</dependency>

To configure the ObjectMapper, I had to modify the CRISP resource configuration XML. The SimpleJacksonRestTemplateResourceResolver has an objectMapper property which can be used to provide a different ObjectMapper:

<bean parent="abstractCrispSimpleJacksonRestTemplateResourceResolver"
      class="org.onehippo.cms7.crisp.core.resource.jackson.SimpleJacksonRestTemplateResourceResolver">

  <!-- ... -->
  <property name="objectMapper">
    <bean id="test"
          class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
      <property name="targetClass"
                value="com.damirscorner.blog.samples.utils.ObjectMapperFactory" />
      <property name="targetMethod" value="getObjectMapper" />
    </bean>
  </property>
</bean>

To make my work easier, I invoked a factory method inside which I configure the ObjectMapper correctly:

public class ObjectMapperFactory {

    private static ObjectMapper mapper;

    public static ObjectMapper getObjectMapper() {
        if (mapper == null)
        {
            mapper = new ObjectMapper();
            mapper.registerModule(new JavaTimeModule());
        }
        return mapper;
    }
}

With all that set up, the ResourceBeanMapper now used my custom ObjectMapper for deserialization which resolved my issue with the LocalDate.

Copyright
Creative Commons License