Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ObjectMapper - Best practice for thread-safety and performance

Summary

I want to find the best practice for using ObjectMapper and/or ObjectReader in terms of thread-safety and performance in the context of the use-case described below.

Background

I have a helper class (Json.java) where the method toObject() uses ObjectMapper to translate from a json string to an object of a given (json-mappable) class.

Problem / question

I have read that ObjectReader is often recommended for being fully thread-safe, but I mostly see it being in a non-generic context where the class to read for is predefined. In this context, what do you believe to be the best practice in terms of thread-safety and performance? In the code I have three suggestions that I could think of as a starting point.

I have tried to look at through the source code and docs for jackson-databind, but my theoretical Java skills are not good enough to derive an answer from them. I have also looked at similar questions on SO and elsewhere, but haven't found any that match my case closely enough.

import java.io.IOException;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;

public abstract class Json {

    private static final ObjectMapper jsonMapper = new ObjectMapper();
    
    // NOTE: jsonReader is only relevant for Suggestion 3.
    private static final ObjectReader jsonReader = jsonMapper.reader(); 

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

    // 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);
    }

    // Remainder of class omitted for brevity.
}
like image 468
axeleration Avatar asked Aug 27 '19 08:08

axeleration


People also ask

Is ObjectMapper Jackson thread-safe?

Jackson's ObjectMapper is completely thread safe and should not be re-instantiated every time #2170.

Should ObjectMapper be reused?

Creating an ObjectMapper is quite expensive. Therefore it is recommended that you reuse your ObjectMapper instance. The Jackon ObjectMapper is thread-safe so it can safely be reused.

Should Jackson ObjectMapper be static?

Yes, that is safe and recommended.

Why do we use ObjectMapper?

ObjectMapper provides functionality for reading and writing JSON, either to and from basic POJOs (Plain Old Java Objects), or to and from a general-purpose JSON Tree Model ( JsonNode ), as well as related functionality for performing conversions.

Is objectmapper thread safe?

Although ObjectMapper is thread safe, I would strongly discourage from declaring it as a static variable, especially in multithreaded application. Not even because it is a bad practice, but because you are running a heavy risk of deadlocking. I am telling it from my own experience.

Is objectreader thread-safe?

I have read that ObjectReader is often recommended for being fully thread-safe, but I mostly see it being in a non-generic context where the class to read for is predefined. In this context, what do you believe to be the best practice in terms of thread-safety and performance?

Is it safe to declare a static objectmapper in Java?

Although it is safe to declare a static ObjectMapper in terms of thread safety, you should be aware that constructing static Object variables in Java is considered bad practice. For more details, see Why are static variables considered evil? (and if you'd like, my answer)

What is the difference between mapper and objectreader?

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 :


3 Answers

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.

like image 132
Andrew Tobilko Avatar answered Oct 17 '22 06:10

Andrew Tobilko


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)

like image 29
davidxxx Avatar answered Oct 17 '22 04:10

davidxxx


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>
like image 4
Sharon Ben Asher Avatar answered Oct 17 '22 04:10

Sharon Ben Asher