杰克逊:如何防止字段序列化

188

我有一个实体类,其中包含一个密码字段:

class User {
    private String password;

    //setter, getter..
}

我希望在序列化时跳过这个字段,但仍然能够反序列化。这很重要,以便客户端可以发送给我一个新的密码,但不能读取当前的密码。

如何使用Jackson实现?


1
你不想序列化它,但是你想能够反序列化它?我认为这是不可能的。如果你不把饼干放在盒子里,你就无法从这个盒子中取出它。 - Alexis Dufrenoy
1
@Traroth:但是我可以放一个新的cookie。我只是在寻找一个方便的注释,但这肯定可以手动完成。 - weekens
8
快速评论:从技术上讲,完全有可能拥有一个被使用的 setter(即使是私有的也会自动检测),然后省略访问器(没有公共字段或 getter)。还可以在 getter 上添加 @JsonIgnore,但在 setter 上添加 @JsonProperty,这样就不会序列化,但可以反序列化。 - StaxMan
4
你介意接受这个问题的答案吗?(你还有几个问题几年前提出的,但仍未被采纳...请考虑一下!) :) 全面公开 - 我在任何一个问题上都没有答案。 - Barett
1
这个回答解决了你的问题吗?仅在序列化期间使用@JsonIgnore,而不是反序列化 - kulsin
动态忽略序列化期间的字段选项:https://dev59.com/pGAg5IYBdhLWcg3w1uDy#74594018 - Taras Shpek
9个回答

219

您可以将其标记为@JsonIgnore

从1.9版本开始,您可以为getter添加@JsonIgnore,为setter添加@JsonProperty,以使其反序列化但不进行序列化。


6
@JsonIgnore 会阻止反序列化。对于 transient 关键字,我会进行确认。 - weekens
108
在1.9版本中,您可以在getter方法上添加@JsonIgnore注解,在setter方法上添加@JsonProperty注解,以使其在反序列化时生效但不会被序列化。 - StaxMan
StaxMan的评论应该是答案,不是吗?那为什么还是个评论呢...!..? - Saravanabalagi Ramachandran
transient 对我似乎不起作用,我标记了字段“transient”,表示我不希望它被序列化/反序列化。 - rohith
此前的答案建议使用 transient(例如 private transient String password;),但是除非启用了 MapperFeature.PROPAGATE_TRANSIENT_MARKER,否则具有公共 getter 的 transient 字段将被忽略。 - tom
在字段和getter上使用@JsonIgnore,在setter上使用@JsonProperty会使单一属性进行反序列化而不进行序列化。然而,当我在List<Foo>字段上执行相同操作时,它并不能反序列化?有什么解决方法吗? - Farrukh Najmi

103

补充一下StaxMan所说的,这对我有效

private String password;

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

@JsonProperty
public void setPassword(String password) {
    this.password = password;
}

13
你使用哪个Json依赖?这不能与com.fasterxml.jackson一起使用。 - Alexander Burakevych
3
谢谢!这个字段似乎不需要@JsonIgnore - Ferran Maylinch
com.fasterxml.jackson.annotation.JsonIgnore 来自于 jackson-annotations-<version>.jar - mvmn
我已经尝试过了,但对我没有用。我使用的是v2.8版本。有什么帮助吗? - Maz
对我来说不起作用。 - Raj

43

Jackson 2.6开始,属性可以被标记为只读或只写。这比在两个访问器上操作注释更简单,并将所有信息保存在一个位置:

public class User {
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private String password;
}

这是一个很棒的解决方案,简单而且有效。谢谢。 - dhqvinh
是的。谢谢。出于某些原因,常规的在setter上使用@JsonProperty和在getter上使用@JsonIgnore绝对不适用于我。我想不出为什么,但这个方案解决了我的问题。谢谢。 - Forrest
StackOverflow应该有多个赞,每次我忘记这个注释时,SO都会带回你的答案,谢谢,伙计。 - Neeraj Jain

40
简单的方法是注释你的getter和setter。 下面是修改后的原始示例,排除了纯文本密码,但注释了一个新方法,该方法只返回加密文本中的密码字段。
class User {

    private String password;

    public void setPassword(String password) {
        this.password = password;
    }

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

    @JsonProperty("password")
    public String getEncryptedPassword() {
        // encryption logic
    }
}

20

除了@JsonIgnore外,还有其他几种可能性:

  • 使用JSON Views 条件性地过滤字段(默认情况下,不用于反序列化;在2.0中将可用,但您可以在序列化、反序列化时使用不同的视图)
  • @JsonIgnoreProperties 可能对类有用

13

transient对我来说是一个解决方案。谢谢!它是Java的本地功能,避免了您添加另一个特定于框架的注释。


2
如果你的属性确实是瞬态的,不涉及ORM,那么这种方法可以使用...在我的情况下,我发现@JsonIgnore更有趣,尽管我将被锁定为jackson,但这是一个很好的权衡。 - Joao Pereira
3
如果你创建自己的注解,并使用@JsonIgnore和@JacksonAnnotationsInside进行注释,就不必被锁定于Jackson。这样,如果你更改序列化器,只需更改自己的注解即可。 - DavidA

5
Jackson有一个名为SimpleBeanPropertyFilter的类,可以帮助在序列化和反序列化期间过滤字段; 不是全局的。我想这就是你想要的。
@JsonFilter("custom_serializer")
class User {
    private String password;

    //setter, getter..
}

然后在你的代码中:
String[] fieldsToSkip = new String[] { "password" };

ObjectMapper mapper = new ObjectMapper();

final SimpleFilterProvider filter = new SimpleFilterProvider();
filter.addFilter("custom_serializer",
            SimpleBeanPropertyFilter.serializeAllExcept(fieldsToSkip));

mapper.setFilters(filter);

String jsonStr = mapper.writeValueAsString(currentUser);

这将防止password字段被序列化。同时,您将能够将password字段反序列化为原始值。只需确保在ObjectMapper对象上未应用任何过滤器即可。
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(yourJsonStr, User.class);    // user object does have non-null password field

4

一个人应该问为什么要为密码创建一个公共的getter方法。Hibernate或任何其他ORM框架都可以使用私有getter方法。如果需要检查密码是否正确,可以使用

public boolean checkPassword(String password){
  return this.password.equals(anyHashingMethod(password));
}

我认为这是思考解决方案的最佳方式。 将需要的东西设为私有,并从对象本身控制它们更有意义。 - arcynum

1

将变量设置为

@JsonIgnore

这样可以使变量被json序列化器跳过。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接