Hi All,
First - thanks so much for the work on Ektorp. It has really allowed me to hit the ground running and produce something valuable for my project quickly. Its familiar interface makes CouchDb a viable option for folks with Java backgrounds.
I have a question about the expected behavior of StdObjectMapperFactory#setObjectMapper. On the surface, it seems like this would support global config of a given ObjectMapper. In practice, this doesn't always happen.
There are a few related, but distinct items here:
- The upgrade from 1.3.0 to 1.4.x had a regression that resulted in null values no longer being ignored on serialization.
- This confused me until I dug into the code
- The bug resulted in the tutorial being broken, which impacted at least one person - https://github.com/helun/Ektorp/issues/230
- A fix for restoring this behavior alone is in https://github.com/helun/Ektorp/pull/233
- Configuration Responsibility -
- YannROBERT made a great point: "IMO It's up to the application developer to configure this on a per class basis. Ektorp should not enforce this for every classes." https://github.com/helun/Ektorp/pull/233#issuecomment-83498432
- I think that's a very valid point, it seems outside of the responsibility of the ObjectMapperFactory to configure the ObjectMapper when a developer has the option to pass in a configured ObjectMapper.
I'd really enjoy contributing the code here, but being new to the project, I want to make sure I'm on the same page as the more established members of the community:
- Does it make sense to fix the non-null regression in isolation?
- If the StdObjectMapperFactory should respect the given ObjectMapper in all cases:
- should we update that class (making a potentially breaking change)?
- or should we introduce a new ObjectMapperFactory implementation and leave the current one unchanged? (proposal below)
Background
There are 2 methods on the ObjectMapperFactory interface:
[1] ObjectMapper createObjectMapper();
[2] ObjectMapper createObjectMapper(CouchDbConnector);
In the current implementation of StdObjectMapperFactory, the variable set by calling setObjectMapper is only used by #1. #2 always creates
a new ObjectMapper, ignoring the variable. I gather this is why the implementation of #1 is synchronized, while #2 is not.
[1] is called from:
- new StdCouchDbInstance(HttpClient)
- new StdCouchDbInstance(HttpClient, ObjectMapperFactory)
- new ViewQuery()
[2] is called from:
- new StdCouchDbConnector(String, CouchDbInstance)
- new StdCouchDbConnector(String, CouchDbInstance, ObjectMapperFactory)
In the application I've been writing, I'm always using a CouchDbConnector or an extension of CouchDbRepositorySupport<T>, which depends on a CouchDbConnector. So any global configuration I do, for example making DateTime serialize and deserialize from an ISO date string instead of the
default bean-serialization, doesn't take effect here.
Proposal
In my application I've reimplemented the interface to respect the given ObjectMapper in both situations. I think this could be beneficial to others.
```
public class ImmutableObjectMapperFactory implements ObjectMapperFactory {
private final ObjectMapper objectMapper;
public ImmutableObjectMapperFactory() {
this(new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL));
}
public ImmutableObjectMapperFactory(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override public ObjectMapper createObjectMapper() {
return objectMapper;
}
@Override public ObjectMapper createObjectMapper(CouchDbConnector connector) {
ObjectMapper copy = objectMapper.copy();
copy.registerModule(new EktorpJacksonModule(connector, copy));
return copy;
}
}
```
I like this for a few reasons:
- The passed-in, configured object mapper is always used.
- synchronization is now unnecessary because the mapper is immutable.
- Default ignore-null configuration is still included if you call the parameterless constructor, so the tutorial would remain working unchanged.
- The EktorpJacksonModule is still added only for the instances used by the connector, which seems to mostly exist for the @DocumentReferences annotation.
Best,
Steve