Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@JsonIgnore has no effect when using Lombok @Getter #1226

Closed
sivasankariit opened this issue May 11, 2016 · 8 comments
Closed

@JsonIgnore has no effect when using Lombok @Getter #1226

sivasankariit opened this issue May 11, 2016 · 8 comments

Comments

@sivasankariit
Copy link

sivasankariit commented May 11, 2016

When using the @Getter lombok annotation on a class along with @JsonIgnore on class fields, the @JsonIgnore has no effect during serialization. This is a change in behavior when upgrading from jackson-databind v2.6.4 to v.2.7.4. I've added a sample test case below with the serialized outputs in each case.

import org.testng.annotations.Test;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.SneakyThrows;

/**
 * Test that shows the difference between serialization with jackson-databind v2.6.4 vs v2.7.4
 *
 * <pre>
 *     With v2.6.4:
 *     {
 *         "fieldB" : "b1",
 *         "a" : "a1"
 *     }
 *
 *     With v2.7.4:
 *     {
 *         "fieldA" : "a1",
 *         "fieldB" : "b1",
 *         "a" : "a1"
 *     }
 * </pre>
 */
public class SimpleTest {

    @AllArgsConstructor
    @Getter
    private static class Obj {

        @JsonIgnore
        private final String fieldA;

        @JsonProperty
        private final String fieldB;

        @JsonProperty("a")
        private String getFieldAStr() {
            return fieldA;
        }
    }

    @Test
    @SneakyThrows
    public void testSerDes() {
        Obj o1 = new Obj("a1", "b1");

        ObjectMapper mapper = new ObjectMapper();
        DefaultPrettyPrinter pp = new DefaultPrettyPrinter();
        pp.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE);
        pp.indentObjectsWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE);
        ObjectWriter writer = mapper.writer().with(pp);

        String str = writer.writeValueAsString(o1);
        System.out.println(str);
    }
}

@cowtowncoder
Copy link
Member

Unfortunately I would need a test that does not make use of Lombok; perhaps generated class, if possible. Your usage looks correct and I would expect fieldA not to be included (it's private anyway).
I am guessing something with @AllArgsConstructor creates annotations that cause the behavior change, but I would need to reproduce exact effects.

@sivasankariit
Copy link
Author

Here is the class after the Lombok code generation:

import org.testng.annotations.Test;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

/**
 * Test that shows the difference between serialization with jackson-databind v2.6.4 vs v2.7.4
 *
 * <pre>
 *     With v2.6.4:
 *     {
 *         "fieldB" : "b1",
 *         "a" : "a1"
 *     }
 *
 *     With v2.7.4:
 *     {
 *         "fieldA" : "a1",
 *         "fieldB" : "b1",
 *         "a" : "a1"
 *     }
 * </pre>
 */
public class SimpleTest {

    private static class Obj {

        @JsonIgnore
        private final String fieldA;

        @JsonProperty
        private final String fieldB;

        @java.beans.ConstructorProperties({"fieldA", "fieldB"})
        Obj(String fieldA, String fieldB) {
            this.fieldA = fieldA;
            this.fieldB = fieldB;
        }

        @JsonProperty("a")
        private String getFieldAStr() {
            return fieldA;
        }

        public String getFieldA() {
            return this.fieldA;
        }

        public String getFieldB() {
            return this.fieldB;
        }
    }

    @Test
    public void testSerDes() throws JsonProcessingException {
        Obj o1 = new Obj("a1", "b1");

        ObjectMapper mapper = new ObjectMapper();
        DefaultPrettyPrinter pp = new DefaultPrettyPrinter();
        pp.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE);
        pp.indentObjectsWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE);
        ObjectWriter writer = mapper.writer().with(pp);

        String str = writer.writeValueAsString(o1);
        System.out.println(str);
    }
}

@romansergey
Copy link

I've experienced the same issue with 2.7.3. Employing @JsonIgnoreProperties on class level instead of @JsonIgnore helped work around it.

@cowtowncoder is that an expected change in behavior?

@minbo
Copy link

minbo commented Sep 16, 2016

@sivasankariit
Lombok puts @ConstructorProperties to the all arguments constructor. The field properties are overwritten by the constructor properties during creating JSON serializer. This happens in POJOPropertiesCollector.collectAll (I searched the code of jackson-databind 2.8.2).

An easy solution would be to set suppressConstructorProperties as true in @AllArgsConstructor.

@cowtowncoder
Copy link
Member

Ah ok, was missing the produced class.

So, seeing the definition I do understand why things work the way they do.
It is bit complicated... bear with me.

So: what happens with respect to fieldA is that there are 3 (potential) accessors:

  • Field fieldA, marked as ignored
  • Getter getFieldA(), visible but neither explicitly included or ignored
  • Creator due to @ConstructorProperties. As of 2.7 (or earlier with jdk7 datatype); explicitly included -- this is similar to using @JsonCreator

Now... due to having both ignoral and inclusion (due to explicit creator), handling considers it a "split" case. As such, getter is "pulled in" and considered visible.

One way to prevent this would be to move (or just add) @JsonIgnore to getter method (field is actually not visible and won't be used). That should prevent serialization of fieldA

Alternatively, there is the question of whether @ConstructorProperties should or should not be considered alias for @JsonCreator. In 2.9, there will be new MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES, disabling of which will disable handling (see #1371 for details).

Third way is to prevent Lombok from adding the annotation itself. This may or may not make sense, depending on what intent is.

So at this point I have to say that yes, this is the expected behavior as of 2.7 and beyond.

@romansergey
Copy link

@cowtowncoder, it totally makes sense now. Thank you for elaboration!

@ollifer
Copy link

ollifer commented May 31, 2018

Please take a look to combination @JsonIgnoreProperties (value={"field1"}) and @AllArgsConstructor it also doesn't work

@gregbown
Copy link

@ollifer used @Getter(onMethod = @__( @JsonIgnore )) in my @AllArgsConstructor implementation and it worked perfectly. See Lombok documentation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants