Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I force Jackson to use an Object's Setters?

Tags:

java

jackson

Given I have the following JSON file:

{
    "cleanup": false,
    "clearCache": false
}

and the following bean:

import org.apache.commons.lang3.Validate;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Config implements IHoldConfiguration {

    public static final boolean DEFAULT_CLEANUP = false;
    public static final boolean DEFAULT_CLEAR_CACHE = false;
    public static final int DEFAULT_CONCURRENCY = 1;

    @JsonProperty(value = "cleanup", required = false, defaultValue = "false")
    private Boolean cleanup;

    @JsonProperty(value = "clearCache", required = false, defaultValue = "false")
    private Boolean clearCache;

    @JsonProperty(value = "concurrency", required = false, defaultValue = "1")
    private Integer concurrency;

    public boolean isCleanup() {
        return this.cleanup.booleanValue();
    }

    public void setCleanup(final Boolean cleanup) {
        this.cleanup = cleanup != null ? cleanup : DEFAULT_CLEANUP;
    }

    public boolean isClearCache() {
        return this.clearCache.booleanValue();
    }

    public void setClearCache(final Boolean clearCache) {
        this.clearCache = clearCache != null ? clearCache : DEFAULT_CLEAR_CACHE;
    }

    public int getConcurrency() {
        return this.concurrency.intValue();
    }

    public void setConcurrency(Integer concurrency) {

        if (concurrency == null) {
            concurrency = DEFAULT_CONCURRENCY;
        } else {
            Validate.inclusiveBetween(1, Integer.MAX_VALUE, concurrency,
                    String.format("concurrency must be in range [1, %s]", Integer.MAX_VALUE));
        }

        this.concurrency = concurrency;
    }
}

How do I force Jackson 2.9.2 to use my concurrency setter? Currently, when deserialized from JSON to bean, using new ObjectMapper().readValue(inputStream, Config.class), concurrency is set to null.

I thought that Jackson used the setters in a bean, if provided, to set a property. After seeing the deserialized result, and debugging, I've found this is not true. Since the defaultValue aatribute of JsonProperty is for documentation only, I thought I could add default value setting in the setters, but obviously this did not work. How can I accomplish this?

like image 673
liltitus27 Avatar asked Dec 19 '17 21:12

liltitus27


2 Answers

The problem is that since concurrency is not declared in your json string the setter isn't being called. If you had

{
    cleanup: false,
    clearCache: false,
    concurrency: null
}

Then the setter would be called with a null argument and you set your default value.

To get a default without tweaking the json string you could do a couple of things. Firstly you can simply set the default on the variable when it is declared:

public class Config implements IHoldConfiguration {
    ...
    public static final int DEFAULT_CONCURRENCY = 1;

    ...
    private Integer concurrency = Integer.valueOf(DEFAULT_CONCURRENCY);

Now it doesn't matter that Jackson doesn't try to set concurrency because it will already be constructed with the correct value. Alternatively you could force Jackson to use a constructor and it will pass all the values even those that are not in the json string.

So for example you could add a constructor like:

@JsonCreator
public Config (@JsonProperty("cleanup") Boolean cleanup,
               @JsonProperty("clearCache") Boolean clearCache,
               @JsonProperty("concurrency") Integer concurrency) {
    ....

And put your logic in there.

like image 172
Evan Jones Avatar answered Nov 14 '22 23:11

Evan Jones


You can not force it to be called because there is nothing to call it with.

The usual way to set defaults is to either directly assign value to field in declaration, like:

private Integer concurrency = Integer.valueOf(1);

or do this in default constructor.

like image 20
StaxMan Avatar answered Nov 14 '22 22:11

StaxMan