Only using @JsonIgnore during serialization, but not deserialization
Asked Answered
I

10

446

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
Interpenetrate answered 20/9, 2012 at 1:49 Comment(0)
S
673

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.

Sitology answered 20/9, 2012 at 1:53 Comment(9)
gist.github.com/thurloat/2510887 for Jackson JSON ignore on deserialize onlyOldtimer
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.Therron
@Matt : Cant you use "transient" then ?Brennan
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 AFAIKTherron
Also, make sure to remove @JsonProperty from the field itself otherwise it will override your getter/setter annotationsBidentate
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)Mephitis
Jackson-annotations 2.5 does not have the later feature. Jackson-annotations 2.6.3 doesSemifinal
fasterxml.github.io/jackson-annotations/javadoc/2.6/com/… Yes the enum of Access in JsonProperty is since 2.6Rizal
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.Drusilladrusus
B
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;
    }
}
Brennan answered 9/12, 2014 at 18:37 Comment(1)
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.Boehmer
I
110

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.

Islam answered 8/12, 2015 at 14:22 Comment(5)
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.Luciolucita
This is available since version 2.6, see fasterxml.github.io/jackson-annotations/javadoc/2.6/com/…Islam
Probably the best answer for 2.6+ users.Showers
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.Waxwork
WRITE_ONLY, what a terrible name when your goal is to write your java object into a json file. DESERIALIZE_ONLY would be unambiguousPresumably
S
17

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.

Stegosaur answered 22/6, 2015 at 15:45 Comment(1)
this is an extremely useful answer that really helped me find the source of why @JsonIgnore was not being honored by Jackson... thanks!Receptionist
C
8

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.

Classieclassification answered 10/12, 2019 at 23:11 Comment(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!Classieclassification
R
3
    "user": {
        "firstName": "Musa",
        "lastName": "Aliyev",
        "email": "[email protected]",
        "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
    }
Roundelay answered 1/5, 2016 at 11:13 Comment(2)
It might help in future to give even a brief description of how the code works.Resentment
Alright !! What are you trying here ? Getting more votes ? lolBrennan
B
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;
    }
Beadledom answered 6/7, 2020 at 8:54 Comment(0)
S
2

You can also do like:

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

It's worked for me

Stotinka answered 22/8, 2020 at 16:28 Comment(1)
It will completely ignore. This is not what asked in question. JsonIgnore has precedence over JsonProperty.Cahoon
B
1

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;
    }
}
Beaverboard answered 20/4, 2022 at 19:21 Comment(0)
W
-2

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

Wojak answered 20/1, 2023 at 12:59 Comment(2)
That is NOT ideal. It's just busy work that create parallel hierarchies which increases maintenance costs and bugs.Interpenetrate
@Interpenetrate Ideal in the sense that it organizes a reliable contract. This, of course, comes with costs.Wojak

© 2022 - 2024 — McMap. All rights reserved.