Looking for some kind of "catch all" in your Jackson deserialization efforts? 

Want an object that catches everything that wasn't specifically mapped to a setter property?

Have another Spring Boot use case for this subject that I didn't think about?

In any of those situations, you're in luck. Jackson (and, by extension, Spring Boot) offers an annotation that enables you to map properties from a JSON object to a Map object.

I'll show you how to use it in this guide.

Use @JsonAnySetter

Yes, the title was a spoiler.

Here's what you need to do: create a method that enables other objects to add (or put) something in the Map object. Then, annotate that method with @JsonAnySetter.

Any property that isn't specified in the class gets plopped right into that Map. It's as easy as that.

It's a nifty way to capture the stuff that's in the JSON object but not in the Java object.

A Simple Test

Let's try a thing.

Here's an Employee class that exposes the ID of the employee. It's also got a Map object that holds supplemental properties.

private static class Employee {
    
    private String id;
    private Map<String, Object> supplementalFields = new HashMap<>();

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Map<String, Object> getSupplementalFields() {
        return supplementalFields;
    }

    public void addSupplementalField(String property, Object value) {
        this.supplementalFields.put(property, value);
    }
    
    public String toString() {
        return ReflectionToStringBuilder.toString(this);
    }
}

As you can see, no annotations in there. So we're going to try this without using @JsonAnySetter first.

Also pay attention to that addSupplementalField() method. That method just takes in a key/value pair and adds them to the supplementalFields map.

That's also the method that we'll decorate later on.

And note how the toString() method gives us a pretty printout of the whole object by using Apache's ReflectionToStringBuilder. That makes it easy to check our work.

Now let's take this class for a test drive with this code:

String json = "{\r\n"
        + "  \"id\" : \"A17\",\r\n"
        + "  \"service\" : \"Army\",\r\n"
        + "  \"disabled\" : true\r\n"
        + "}";
            
ObjectMapper mapper = new ObjectMapper();
Employee employee = mapper.readerFor(Employee.class).readValue(json);
System.err.println(employee);

So that creates a String called json that looks like this:

{
  "id" : "A17",
  "service" : "Army",
  "disabled" : true
}

You'll note that it has three (3) properties: "id," "service," and "disabled." But the Employee class only has one (1) of those properties defined: "id."

Next, the code instantiates our friend the ObjectMapper. Then, it uses that object to deserialize the JSON string above.

Finally, it prints out the Employee object using the pretty printing that I explained previously.

So now let's run this thing and see what we get.

[Exception stacktrace prints out]

Oh.

We get an UnrecognizedPropertyException.

Specifically, we get that method on the "service" property. That's because there's no corresponding property named "service" in the Employee class.

So we should get that error.

Now let's fix it.

Making It Right

Not much work to do here. Just add that @JsonAnySetter annotation on top of the addSupplementalField() method like so:

...
    @JsonAnySetter
    public void addSupplementalField(String property, Object value) {
        this.supplementalFields.put(property, value);
    }
...

And run the code from above again. Now you'll see this result (or something like it):

Employee@39529185[id=A17,supplementalFields={service=Army, disabled=true}]

There you go. No errors.

So what happened?

Here's what happened: the Map object got populated with the two properties that weren't explicitly defined in the Employee class.

That is, in fact, what @JsonAnySetter will buy you.

Wrapping It Up

Excellent. You now know how to use the @JsonAnySetter Jackson annotation.

Feel free to include it in your upcoming Spring Boot applications.

Have fun!

Daria Nepriakhina on Unsplash