438

I have a user object that is sent to and from the server. When I send out the user object, I don't want to send the hashed password to the client. So, I added @JsonIgnore on the password property, but this also blocks it from being deserialized into the password that makes it hard to sign up users when they don't have a password.

How can I only get @JsonIgnore to apply to serialization and not deserialization? I'm using Spring JSONView, so I don't have a ton of control over the ObjectMapper.

Things I've tried:

  1. Add @JsonIgnore to the property
  2. Add @JsonIgnore on the getter method only
rumdrums
  • 1,322
  • 2
  • 11
  • 25
chubbsondubs
  • 37,646
  • 24
  • 106
  • 138

10 Answers10

653

Exactly how to do this depends on the version of Jackson that you're using. This changed around version 1.9, before that, you could do this by adding @JsonIgnore to the getter.

Which you've tried:

Add @JsonIgnore on the getter method only

Do this, and also add a specific @JsonProperty annotation for your JSON "password" field name to the setter method for the password on your object.

More recent versions of Jackson have added READ_ONLY and WRITE_ONLY annotation arguments for JsonProperty. So you could also do something like:

@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;

Docs can be found here.

Maifee Ul Asad
  • 3,992
  • 6
  • 38
  • 86
pb2q
  • 58,613
  • 19
  • 146
  • 147
  • 5
    https://gist.github.com/thurloat/2510887 for Jackson JSON ignore on **deserialize** only – Hadas Nov 26 '14 at 10:15
  • It works, but I don't want a setter. I can make it no-op, but that feels wrong too. Also, the requirement of a default constructor breaks encapsulation. I just get a lot of dead setters in my code. But I suppose there is no way around this. – Matt Broekhuis Dec 09 '14 at 05:16
  • @Matt : Cant you use "transient" then ? – Balaji Boggaram Ramanarayan Dec 09 '14 at 18:40
  • 1
    AFAIK you still have to implement the setter and annotate it. I just want the getter for serialization. What is the transient you speak of? That's a JPA annotation AFAIK – Matt Broekhuis Dec 10 '14 at 00:37
  • 3
    Also, make sure to remove @JsonProperty from the field itself otherwise it will override your getter/setter annotations – Anton Soradoi Mar 04 '15 at 15:14
  • 28
    `@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)` – Mikhail Batcer Mar 15 '16 at 07:55
  • 1
    Jackson-annotations 2.5 does not have the later feature. Jackson-annotations 2.6.3 does – radiantRazor Oct 21 '16 at 06:50
  • http://fasterxml.github.io/jackson-annotations/javadoc/2.6/com/fasterxml/jackson/annotation/JsonProperty.Access.html Yes the enum of Access in JsonProperty is since 2.6 – Xin Meng Nov 14 '16 at 14:09
  • 2
    Be careful, `JsonProperty` is `com.fasterxml.jackson.annotation.JsonProperty`, `Access` is `com.fasterxml.jackson.annotation.JsonProperty.Access` not `javax.persistence.Access`, If you wrote code like `import javax.persistence.*;` the Access may not what you want. – Donghua Liu May 28 '18 at 02:52
117

In order to accomplish this, all that we need is two annotations:

  1. @JsonIgnore
  2. @JsonProperty

Use @JsonIgnore on the class member and its getter, and @JsonProperty on its setter. A sample illustration would help to do this:

class User {

    // More fields here
    @JsonIgnore
    private String password;

    @JsonIgnore
    public String getPassword() {
        return password;
    }

    @JsonProperty
    public void setPassword(final String password) {
        this.password = password;
    }
}
Sae1962
  • 1,122
  • 15
  • 31
  • 4
    it's the only thing that worked for me with jackson 2.6.4. I tried my best to use @JsonProperty(access = Access.WRITE_ONLY) but it didn't work for me. – cirovladimir Feb 17 '17 at 21:37
107

Since version 2.6: a more intuitive way is to use the com.fasterxml.jackson.annotation.JsonProperty annotation on the field:

@JsonProperty(access = Access.WRITE_ONLY)
private String myField;

Even if a getter exists, the field value is excluded from serialization.

JavaDoc says:

/**
 * Access setting that means that the property may only be written (set)
 * for deserialization,
 * but will not be read (get) on serialization, that is, the value of the property
 * is not included in serialization.
 */
WRITE_ONLY

In case you need it the other way around, just use Access.READ_ONLY.

Aaron Kurtzhals
  • 2,036
  • 3
  • 17
  • 21
Daniel Beer
  • 1,709
  • 1
  • 14
  • 15
  • 3
    I don't understand why there so few upvotes, this is elegant way to solve this problem, works like a charm. There is no need to annotate getter,setter and field. Only field. Thanks. – CROSP Dec 26 '15 at 16:22
  • This is available since version 2.6, see http://fasterxml.github.io/jackson-annotations/javadoc/2.6/com/fasterxml/jackson/annotation/JsonProperty.Access.html – Daniel Beer Dec 27 '15 at 22:56
  • Probably the best answer for 2.6+ users. – David Dossot Feb 17 '16 at 02:03
  • Make sure you are not using the **org.codehaus.jackson.annotate** import. @DanielBeer's solution works for Jackson 2.6.3. This should be the accepted answer now that Jackson has been updated. – Kent Bull Mar 11 '16 at 00:17
  • `WRITE_ONLY`, what a terrible name when your goal is to write your java object into a json file. `DESERIALIZE_ONLY` would be unambiguous – Sebastian S Jun 29 '23 at 17:13
16

In my case, I have Jackson automatically (de)serializing objects that I return from a Spring MVC controller (I am using @RestController with Spring 4.1.6). I had to use com.fasterxml.jackson.annotation.JsonIgnore instead of org.codehaus.jackson.annotate.JsonIgnore, as otherwise, it simply did nothing.

Sae1962
  • 1,122
  • 15
  • 31
Alex Beardsley
  • 20,988
  • 15
  • 52
  • 67
  • 1
    this is an extremely useful answer that really helped me find the source of why @JsonIgnore was not being honored by Jackson... thanks! – Clint Eastwood Jun 14 '17 at 21:13
7

Another easy way to handle this is to use the argument allowSetters = true in the annotation. This will allow the password to be deserialized into your dto but it will not serialize it into a response body that uses contains object.

example:

@JsonIgnoreProperties(allowSetters = true, value = {"bar"})
class Pojo{
    String foo;
    String bar;
}

Both foo and bar are populated in the object, but only foo is written into a response body.

Dmitriy Popov
  • 2,150
  • 3
  • 25
  • 34
Dhandley
  • 71
  • 1
  • 1
  • That was my mistake, I didn't realize you had already corrected the snippet. Looking at your changes, it appears my years of writing in Groovy got me and my translation to Java was a bit rough. Sorry! – Dhandley Dec 25 '19 at 22:58
3
    "user": {
        "firstName": "Musa",
        "lastName": "Aliyev",
        "email": "klaudi2012@gmail.com",
        "passwordIn": "98989898", (or encoded version in front if we not using https)
        "country": "Azeribaijan",
        "phone": "+994707702747"
    }

    @CrossOrigin(methods = RequestMethod.POST)
    @RequestMapping("/public/register")
    public @ResponseBody MsgKit registerNewUsert(@RequestBody User u) {
    
        root.registerUser(u);
    
        return new MsgKit("registered");
    }

    @Service
    @Transactional
    public class RootBsn {
    
        @Autowired UserRepository userRepo;
        
        public void registerUser(User u) throws Exception {
    
            u.setPassword(u.getPasswordIn());
            // Generate some salt and setPassword (encoded - salt + password)
            User u = userRepo.save(u);
    
            System.out.println("Registration information saved");
        }       
    }

    @Entity
    @JsonIgnoreProperties({"recordDate", "modificationDate", "status", "createdBy", "modifiedBy", "salt", "password"})
    public class User implements Serializable {
        private static final long serialVersionUID = 1L;
                        
        @Id
        @GeneratedValue(strategy=GenerationType.AUTO)
        private Long id;
                        
        private String country;
                        
        @Column(name = "CREATED_BY")
        private String createdBy;
                        
        private String email;
                        
        @Column(name = "FIRST_NAME")
        private String firstName;
                        
        @Column(name = "LAST_LOGIN_DATE")
        private Timestamp lastLoginDate;
                        
        @Column(name = "LAST_NAME")
        private String lastName;
                        
        @Column(name = "MODIFICATION_DATE")
        private Timestamp modificationDate;
                        
        @Column(name = "MODIFIED_BY")
        private String modifiedBy;
                        
        private String password;
                            
        @Transient
        private String passwordIn;
                        
        private String phone;
                        
        @Column(name = "RECORD_DATE")
        private Timestamp recordDate;
                                
        private String salt;
                        
        private String status;
                        
        @Column(name = "USER_STATUS")
        private String userStatus;
                        
        public User() {
        }

        // getters and setters
    }
Dmitriy Popov
  • 2,150
  • 3
  • 25
  • 34
Musa
  • 2,596
  • 26
  • 25
2

You can use @JsonIgnoreProperties at the class level and put variables you want to ignore in JSON in the value parameter. Worked fine for me.

    @JsonIgnoreProperties(value = { "myVariable1", "myVariable2" })
    public class MyClass {
          private int myVariable1;,
          private int myVariable2;
    }
Dmitriy Popov
  • 2,150
  • 3
  • 25
  • 34
2

You can also do like:

@JsonIgnore
@JsonProperty(access = Access.WRITE_ONLY)
private String password;

It's worked for me

  • 1
    It will completely ignore. This is not what asked in question. JsonIgnore has precedence over JsonProperty. – Mubasher Dec 24 '21 at 10:17
0

I was looking for something similar. I still wanted my property serialized but wanted to alter the value using a different getter. In the below example, I'm deserializing the real password but serializing to a masked password. Here's how to do it:

public class User() {

    private static final String PASSWORD_MASK = "*********";

    @JsonIgnore
    private String password;

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    public String setPassword(String password) {
        if (!password.equals(PASSWORD_MASK) {
            this.password = password;
        }
    }

    public String getPassword() {
        return password;
    }

    @JsonProperty("password")
    public String getPasswordMasked() {
        return PASSWORD_MASK;
    }
}
Collin Krawll
  • 2,210
  • 17
  • 15
-2

The ideal solution would be to use DTO (data transfer object)

vi0
  • 197
  • 1
  • 7
  • That is NOT ideal. It's just busy work that create parallel hierarchies which increases maintenance costs and bugs. – chubbsondubs Jan 20 '23 at 19:41
  • @chubbsondubs Ideal in the sense that it organizes a reliable contract. This, of course, comes with costs. – vi0 Jan 21 '23 at 10:06