选择性地@JsonIgnore Immutables 访问器方法,仅在使用 Jackson 进行序列化或反序列化期间

Posted

技术标签:

【中文标题】选择性地@JsonIgnore Immutables 访问器方法,仅在使用 Jackson 进行序列化或反序列化期间【英文标题】:Selectively @JsonIgnore Immutables accessor methods, ONLY during serialization or deserialization using Jackson 【发布时间】:2019-09-21 17:37:38 【问题描述】:

这绝对不是Only using @JsonIgnore during serialization, but not deserialization 的复制品。问题是一样的,但在 Immutables 的上下文中。

当模型(DTO/DAO)被修饰为不可变时,我无法在序列化期间选择性地@JsonIgnore 属性之一。假设我们有一个 UserDto,它被定义为 Immutable,如下所示

@Value.Immutable
@Value.Style(defaults = @Value.Immutable(copy = false), init = "set*")
@JsonSerialize(as = ImmutableUserDto.class)
@JsonDeserialize(builder = ImmutableUserDto.Builder.class)
public abstract class UserDto 

    @JsonProperty("id")
    @Value.Default
    public int getId() 
        return 0;
    

    @JsonProperty("username")
    public abstract String getUsername();

    @JsonProperty("email")
    public abstract String getEmail();

    @JsonProperty("password")
    public abstract String getPassword();

我相信在序列化期间我们希望忽略服务响应中的password 是公平的。

如果我们使用一个简单的类而不使用 Immutables,那么有很多方法可以实现这一点。例如 - 仅使用 @JsonIgnore 注释 getter。或者如果可能,定义一个不同的访问器方法(没有 get 前缀的东西)并且只定义常规的 setter 方法......等等。

如果我在 Immutables 访问器方法上尝试相同的密码,如下所示:

@Value.Immutable
@Value.Style(defaults = @Value.Immutable(copy = false), init = "set*")
@JsonSersonIgnoreialize(as = ImmutableUserDto.class)
@JsonDeserialize(builder = ImmutableUserDto.Builder.class)
public abstract class UserDto 

    ....

    @JsonProperty("password")
    @JsonIgnore
    public abstract String getPassword();

然后,生成的 ImmutableUserDto 在 getter 和 setter 上添加 @JsonIgnore,如下所示。

@Generated(from = "UserDto", generator = "Immutables")
@SuppressWarnings("all")
@ParametersAreNonnullByDefault
@javax.annotation.Generated("org.immutables.processor.ProxyProcessor")
@Immutable
@CheckReturnValue
public final class ImmutableUserDto extends UserDto 
  ...
  ...
  private final String password;

  ...
  ...

  /**
   * @return The value of the @code password attribute
   */
  @JsonProperty("password")
  @JsonIgnore
  @Override
  public String getPassword() 
    return password;
  

  ...
  ...
  ...

  @Generated(from = "UserDto", generator = "Immutables")
  @NotThreadSafe
  public static final class Builder 

    ...
    ...

    private String password;

    @JsonProperty("password")
    @JsonIgnore
    public final Builder setPassword(String password) 
      this.password = password;
      return this;
    
  

序列化将按预期工作。 password 属性将从 JSON 中排除。但是当我尝试反序列化时,出现以下错误:

java.lang.IllegalStateException: Cannot build UserDto, some of the required attributes are not set [password]

这很明显,因为 Immutables 也将 @JsonIgnore 添加到了 setter 中。

documentation 没有多大帮助。在他们的注意事项部分,它只是提到了以下关于@JsonIgnore

如果使用@JsonIgnore,你应该显式地创建一个属性 非强制性。在 Immutables 中,属性可以声明为 通过@Nullable、Optional 或@Value.Default 进行非强制,这些都是 它们的效果不同,我们不会自动推导出任何东西。

使用@NullableOptional@Value.Default 对于password 之类的字段没有任何用处。

我已经浏览了他们 GitHub 页面上的问题列表,并且有一个 similar issue ,但用户要求使用稍微不同的用例,使用 @Nullable 可以解决在我的情况下不起作用的问题。

我也尝试使用here 的答案之一。仍然导致同样的错误。

看起来这不受 Immutables 库的支持。我自己创建了一个new issue。一旦我从用户那里得到一些关于 SOF 的反馈,我可能会创建一个sscce。

【问题讨论】:

您应该能够使用您链接到的@JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 注释以避免序列化密码值。我已经成功地将它与不可变对象一起使用,以通过 Jackson 影响字段的序列化或反序列化。 【参考方案1】:

我不得不使用@benarena 在这个comment 中给出的建议。但是,我必须明确指定属性的 value 属性以及 Access 属性。

@JsonProperty(value = "password", access = JsonProperty.Access.WRITE_ONLY) 解决了这个问题。

不可变类看起来像:

@Value.Immutable
@Value.Style(defaults = @Value.Immutable(copy = false), init = "set*")
@JsonSersonIgnoreialize(as = ImmutableUserDto.class)
@JsonDeserialize(builder = ImmutableUserDto.Builder.class)
public abstract class UserDto 

    ....

    @JsonProperty(value = "password", access = JsonProperty.Access.WRITE_ONLY)
    public abstract String getPassword();

【讨论】:

以上是关于选择性地@JsonIgnore Immutables 访问器方法,仅在使用 Jackson 进行序列化或反序列化期间的主要内容,如果未能解决你的问题,请参考以下文章

304. Range Sum Query 2D - Immutable

@JsonIgnore

@JsonIgnore 和 @Getter 注解

注释@JsonIgnore的真正用途是啥

JavaScript 中的不可变对象(Immutable Objects)

@JsonIgnore 和 @JsonIgnoreProperties 有啥区别