UnwrappedWithAnySetterTest.java

package tools.jackson.databind.struct;

import java.util.HashMap;
import java.util.Map;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.annotation.*;

import tools.jackson.databind.*;
import tools.jackson.databind.testutil.DatabindTestUtil;

import static org.junit.jupiter.api.Assertions.*;

/**
 * Test for [databind#1811]: {@code @JsonUnwrapped} and {@code @JsonAnySetter}
 * should not cause unwrapped properties to be deserialized twice (once into
 * the unwrapped POJO and again into the any-setter map).
 */
public class UnwrappedWithAnySetterTest extends DatabindTestUtil
{
    static class Outer {
        public Long id;

        @JsonUnwrapped
        public Inner inner;

        private Map<String, Object> extra = new HashMap<>();

        @JsonAnyGetter
        public Map<String, Object> getExtra() {
            return extra;
        }

        @JsonAnySetter
        public void set(String key, Object value) {
            extra.put(key, value);
        }
    }

    static class Inner {
        public String name;
    }

    private final ObjectMapper MAPPER = newJsonMapper();

    // [databind#1811]
    @Test
    public void testUnwrappedWithAnySetter() throws Exception
    {
        String json = "{\"id\":1,\"name\":\"aaa\",\"age\":12}";
        Outer outer = MAPPER.readValue(json, Outer.class);

        // "id" goes to Outer.id
        assertEquals(Long.valueOf(1), outer.id);

        // "name" should ONLY go to the unwrapped Inner, not also to the any-setter map
        assertNotNull(outer.inner);
        assertEquals("aaa", outer.inner.name);

        // "age" is truly unknown, so it should go to the any-setter map
        assertEquals(12, outer.extra.get("age"));

        // Key assertion: "name" must NOT appear in the any-setter map
        // This was the bug in [databind#1811]: "name" was deserialized twice
        assertFalse(outer.extra.containsKey("name"),
                "Property 'name' handled by @JsonUnwrapped should not also appear in @JsonAnySetter map, but extra=" + outer.extra);
        assertEquals(1, outer.extra.size(), "Only 'age' should be in extra map, but got: " + outer.extra);
    }
}