ObjectMapper - Best practice for thread-safety and performance

private static final ObjectMapper jsonMapper = new ObjectMapper();

Constructing an ObjectMapper instance is a relatively expensive operation, so it's recommended to create one object and reuse it. You did it right making it final.

// Suggestion 1:
public static <T> T toObject1(final Class<T> type, final String json) throws IOException {
    return jsonMapper.readValue(json, type);
}

You always read JSON to a POJO, so let's be precise and clear, and use ObjectReader.

// Suggestion 2:
public static <T> T toObject2(final Class<T> type, final String json) throws IOException {
    return jsonMapper.readerFor(type).readValue(json);
}

// Suggestion 3:
public static <T> T toObject3(final Class<T> type, final String json) throws IOException {
    return jsonReader.forType(type).readValue(json);
}

There is no difference, really. Both methods will construct a new ObjectReader object: the former (jsonMapper.readerFor(type)) will give you a fully-built instance directly, the latter (jsonReader.forType(type)) will complement the not-yet-usable jsonReader and returns a ready-to-use object. I would rather go with option 2 because I don't want to keep that field.

You shouldn't worry about performance or thread-safety. Even though creating an ObjectMapper might be costly (or making a copy out of it), getting and working with ObjectReaders is lightweight and completely thread-safe.

From the Java documentation (emphasis mine):

Uses "mutant factory" pattern so that instances are immutable (and thus fully thread-safe with no external synchronization); new instances are constructed for different configurations. Instances are initially constructed by ObjectMapper and can be reused, shared, cached; both because of thread-safety and because instances are relatively light-weight.

I recently had these questions myself and decided on ObjectMapper#reader(InjectableValues) as a factory method. It's very handy particularly when you want to customise an ObjectReader slightly or, as it was in my case, to adjust a DeserializationContext.

That's an excellent question, by the way.


As I mention in the comment, I have always used suggestion #1. I have no knowledge if there is difference between the options in terms of thread safety/performance or at all.

However, this approach will not work if the target type is itself parameterized with generic type. The most obvious usage is some collection:

Json.toObject1(List.class, str);  // will deserialize into List<Object>

for this purpose you will have to use Jackson's TypeReference

// Suggestion 4:
public static <T> T toObject4(final TypeReference<T> typeRef, final String json) throws IOException {
    return jsonMapper.readValue(json, typeRef);
}

Json.toObject4(new TypeReference<List<SomeClass>>(){}, str);  // will deserialize into List<SomeClass>

About concurrency

ObjectMapper versus ObjectReader is not relevant here.
The ObjectReader doesn't look to be helpful for your scenario.
Its specification says :

Builder object that can be used for per-serialization configuration of deserialization parameters, such as root type to use or object to update (instead of constructing new instance).

Note that both instances of ObjectMapper and ObjectReader are thread safe provided that their configuration is not changed between serialization/deserialization client calls.
The ObjectReader specified indeed :

Mapper instances are fully thread-safe provided that ALL configuration of the instance occurs before ANY read or write calls.

While ObjectReader has as difference to be immutable in the way where updating its configuration will make it return a new instance of as stated by its documentation :

Uses "mutant factory" pattern so that instances are immutable (and thus fully thread-safe with no external synchronization); new instances are constructed for different configurations.

In your requirement, you don't want to change the configuration between client calls. So using ObjectMapper looks more relevant.
So I would eliminate the 3) way and also the 2) way since jsonMapper.readerFor(type) that is factory method for ObjectReader instance. Still you don't matter to use an ObjectReader here.

So the simplest and common way looks better :

// Suggestion 1:
public static <T> T toObject1(final Class<T> type, final String json) throws IOException {
    return jsonMapper.readValue(json, type);
 }

About performance

Besides, remember ObjectReader is immutable. So the 2 and 3 ways create new instances of ObjectReader at each call. It doesn't look a good hint for performance.
Yes, these are lightweight objects but creating them at each time has a cost.
The ObjectReader doc says :

Instances are initially constructed by ObjectMapper and can be reused, shared, cached; both because of thread-safety and because instances are relatively light-weight.

There you don't reuse these instances. So you lose any benefit in terms of caching and of performance.
You could store them into a Map field and reuse them but do it only if you need to improve the actual performance of ObjectMapper and of course measure before concluding anything.

Conclusion : for your use case, I think that performance as well as concurrency is better with the first solution (ObjectMapper)