AccessorNamingForBuilderTest.java

package tools.jackson.databind.introspect;

import org.junit.jupiter.api.Test;

import tools.jackson.databind.*;
import tools.jackson.databind.annotation.JsonDeserialize;
import tools.jackson.databind.annotation.JsonPOJOBuilder;
import tools.jackson.databind.exc.UnrecognizedPropertyException;
import tools.jackson.databind.testutil.DatabindTestUtil;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

public class AccessorNamingForBuilderTest extends DatabindTestUtil
{
    @JsonDeserialize(builder=NoPrexixBuilderXY.class)
    static class ValueClassXY
    {
        final int _x, _y;

        protected ValueClassXY(int x, int y) {
            _x = x+1;
            _y = y+1;
        }
    }

    static class NoPrexixBuilderXY
    {
        // non-public so as not to discover without prefix-match
        protected int x, y;

        public NoPrexixBuilderXY x(int x0) {
              this.x = x0;
              return this;
        }

        public NoPrexixBuilderXY y(int y0) {
              this.y = y0;
              return this;
        }

        public ValueClassXY build() {
              return new ValueClassXY(x, y);
        }
    }

    // [databind#2624]: Test with builderPrefix="" on @JsonDeserialize (Lombok-style)
    @JsonDeserialize(builder=NoPrefixBuilderViaAnnotation.NoPrefixBuilder.class, builderPrefix="")
    static class NoPrefixBuilderViaAnnotation
    {
        final int a, b;

        protected NoPrefixBuilderViaAnnotation(int a, int b) {
            this.a = a;
            this.b = b;
        }

        static class NoPrefixBuilder
        {
            protected int a, b;

            public NoPrefixBuilder a(int a0) {
                this.a = a0;
                return this;
            }

            public NoPrefixBuilder b(int b0) {
                this.b = b0;
                return this;
            }

            public NoPrefixBuilderViaAnnotation build() {
                return new NoPrefixBuilderViaAnnotation(a, b);
            }
        }
    }

    // [databind#2624]: Test with custom builderPrefix ("set") on @JsonDeserialize
    @JsonDeserialize(builder=SetPrefixBuilderViaAnnotation.SetPrefixBuilder.class, builderPrefix="set")
    static class SetPrefixBuilderViaAnnotation
    {
        final String name;
        final int value;

        protected SetPrefixBuilderViaAnnotation(String name, int value) {
            this.name = name;
            this.value = value;
        }

        static class SetPrefixBuilder
        {
            protected String name;
            protected int value;

            public SetPrefixBuilder setName(String n) {
                this.name = n;
                return this;
            }

            public SetPrefixBuilder setValue(int v) {
                this.value = v;
                return this;
            }

            public SetPrefixBuilderViaAnnotation build() {
                return new SetPrefixBuilderViaAnnotation(name, value);
            }
        }
    }

    // [databind#2624]: Test that @JsonDeserialize.builderPrefix overrides @JsonPOJOBuilder.withPrefix
    @JsonDeserialize(builder=AnnotationOverrideTest.OverriddenBuilder.class, builderPrefix="")
    static class AnnotationOverrideTest
    {
        final int x;

        protected AnnotationOverrideTest(int x) {
            this.x = x;
        }

        // Builder has @JsonPOJOBuilder(withPrefix="with"), but @JsonDeserialize(builderPrefix="") should win
        @JsonPOJOBuilder(withPrefix="with")
        static class OverriddenBuilder
        {
            protected int x;

            // Using no-prefix method name, not "withX"
            public OverriddenBuilder x(int x0) {
                this.x = x0;
                return this;
            }

            public AnnotationOverrideTest build() {
                return new AnnotationOverrideTest(x);
            }
        }
    }

    // [databind#2624]: Test that @JsonPOJOBuilder.withPrefix still works when builderPrefix not specified
    @JsonDeserialize(builder=FallbackToPojoBuilderTest.PojoBuilder.class)
    static class FallbackToPojoBuilderTest
    {
        final int y;

        protected FallbackToPojoBuilderTest(int y) {
            this.y = y;
        }

        @JsonPOJOBuilder(withPrefix="set")
        static class PojoBuilder
        {
            protected int y;

            public PojoBuilder setY(int y0) {
                this.y = y0;
                return this;
            }

            public FallbackToPojoBuilderTest build() {
                return new FallbackToPojoBuilderTest(y);
            }
        }
    }

    // For [databind#2624]
    @Test
    public void testAccessorCustomWithMethod() throws Exception
    {
        final String json = a2q("{'x':28,'y':72}");
        final ObjectMapper vanillaMapper = jsonMapperBuilder()
                .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).build();

        // First: without custom strategy, will fail:
        try {
            ValueClassXY xy = vanillaMapper.readValue(json, ValueClassXY.class);
            fail("Should not pass, got instance with x="+xy._x+", y="+xy._y);
        } catch (UnrecognizedPropertyException e) {
            verifyException(e, "Unrecognized ");
        }

        // But fine with "no-prefix"
        final ObjectMapper customMapper = jsonMapperBuilder()
                .accessorNaming(new DefaultAccessorNamingStrategy.Provider()
                        .withBuilderPrefix("")
                )
                .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                .build();
        ValueClassXY xy = customMapper.readValue(json, ValueClassXY.class);
        assertEquals(29, xy._x);
        assertEquals(73, xy._y);
    }

    // [databind#2624]: Test @JsonDeserialize.builderPrefix with empty string (Lombok-style)
    @Test
    public void testBuilderPrefixEmptyViaAnnotation() throws Exception
    {
        final ObjectMapper mapper = newJsonMapper();
        final String json = a2q("{'a':10,'b':20}");
        NoPrefixBuilderViaAnnotation result = mapper.readValue(json, NoPrefixBuilderViaAnnotation.class);
        assertEquals(10, result.a);
        assertEquals(20, result.b);
    }

    // [databind#2624]: Test @JsonDeserialize.builderPrefix with custom prefix ("set")
    @Test
    public void testBuilderPrefixCustomViaAnnotation() throws Exception
    {
        final ObjectMapper mapper = newJsonMapper();
        final String json = a2q("{'name':'test','value':42}");
        SetPrefixBuilderViaAnnotation result = mapper.readValue(json, SetPrefixBuilderViaAnnotation.class);
        assertEquals("test", result.name);
        assertEquals(42, result.value);
    }

    // [databind#2624]: Test that @JsonDeserialize.builderPrefix overrides @JsonPOJOBuilder.withPrefix
    @Test
    public void testBuilderPrefixOverridesJsonPOJOBuilder() throws Exception
    {
        final ObjectMapper mapper = newJsonMapper();
        final String json = a2q("{'x':99}");
        AnnotationOverrideTest result = mapper.readValue(json, AnnotationOverrideTest.class);
        assertEquals(99, result.x);
    }

    // [databind#2624]: Test fallback to @JsonPOJOBuilder when builderPrefix not specified
    @Test
    public void testFallbackToJsonPOJOBuilderPrefix() throws Exception
    {
        final ObjectMapper mapper = newJsonMapper();
        final String json = a2q("{'y':55}");
        FallbackToPojoBuilderTest result = mapper.readValue(json, FallbackToPojoBuilderTest.class);
        assertEquals(55, result.y);
    }
}