【问题标题】:How to force Jackson to invoke Set methods of all attributes in object (dto) in controller?如何强制杰克逊在控制器中调用对象(dto)中所有属性的 Set 方法?
【发布时间】:2019-01-15 04:44:54
【问题描述】:

我的“命令”中有一些验证,控制器的参数在每个属性的集合内执行,问题是没有通知属性,杰克逊没有调用 set 方法进行验证。是否可以强制 Jackson 在缺少属性的情况下调用 Set 方法?

例如 Payload 没有代理字段:

{
   "bank": "237",
   "account": "20772-1",
   "taxId": "36456155800",
   "paidAmount": 30.00
}

我的控制器:

public Return confirmTransfer(@RequestBody RechargeTransferConfirmationCommand command) {
    System.out.println(command);
}

Java 类:

public class RechargeTransferConfirmationCommand {

    public static final String ERR_INVALID_AGENCY = "Agency number can not be null.";

    private String bank;
    private String account;
    private String agency;
    private String taxId;

    public RechargeTransferConfirmationCommand(BigDecimal paidAmount, String bank, String account,
            String agency, String taxId) {

        setPaidAmount(paidAmount);
        setBank(bank);
        setAccount(account);
        setAgency(agency);
        setTaxId(taxId);
    }

    public void setRechargeId(RechargeId rechargeId) {
        assertArgumentNotNull(rechargeId, Recharge.ERR_RECHARGE_ID_INVALID);
        this.rechargeId = rechargeId;
    }

    private void setPaidAmount(BigDecimal paidAmount) {
        if (paidAmount == null || paidAmount.compareTo(BigDecimal.ZERO) < 0)
            throw new IllegalArgumentException(Recharge.ERR_INVALID_AMOUNT);
        this.paidAmount = paidAmount;
    }

    private void setBank(String bank) {
        assertArgumentNotEmpty(bank, TransferInformation.ERR_INVALID_BANK_NUMBER);
        this.bank = bank;
    }

    private void setAccount(String account) {
        assertArgumentNotEmpty(account, TransferInformation.ERR_INVALID_ACCOUNT);
        this.account = account;
    }

    private void setAgency(String agency) {
        assertArgumentNotEmpty(agency, ERR_INVALID_AGENCY);
        this.agency = agency;
    }

    private void setTaxId(String taxId) {
        assertArgumentNotEmpty(taxId, ERR_INVALID_TAX_ID);
        this.taxId = taxId;
    }

}

在这种情况下,对于每个字段,都会调用 set 方法进行验证,除了负载中未通知的代理字段,它应该很快抛出方法 assertArgumentNotEmpty 中包含的异常。

【问题讨论】:

  • 为什么不使用@NotNull@Min 注释来处理验证?
  • 因为使用这个注解,客户端的返回对象是特定于框架的,我需要一个自定义的返回。这是我的异常,我可以在 @ControllerAdvice 类中捕获并返回更友好的响应。
  • 如果您的 javax 验证失败,那么您可以拦截 @ControllerAdvice。即使在 javax 验证中,您也可以使用传递验证失败消息的消息

标签: java spring-mvc spring-boot jackson


【解决方案1】:

使用@JsonCreator解决问题,谢谢大家的帮助

@JsonCreator
public RechargeTransferConfirmationCommand(@JsonProperty("paidAmount") BigDecimal paidAmount,
        @JsonProperty("bank") String bank, @JsonProperty("account") String account,
        @JsonProperty("agency") String agency, @JsonProperty("taxId") String taxId) {

    setPaidAmount(paidAmount);
    setBank(bank);
    setAccount(account);
    setAgency(agency);
    setTaxId(taxId);
}

【讨论】:

    【解决方案2】:

    在方法中使用@JsonSetter && 也声明no args constructor

    public RechargeTransferConfirmationCommand(){
    }
    @JsonSetter
        public void setRechargeId(RechargeId rechargeId) {
            assertArgumentNotNull(rechargeId, Recharge.ERR_RECHARGE_ID_INVALID);
            this.rechargeId = rechargeId;
        }
    

    但最好使用 javax 验证。查看this了解详情。

    另一种方式: 您可以在一种方法中编写所有验证并使用@AssertTrue:

    @AssertTrue
    public boolean isValid(){
    //if all field is valid then return true;
    return false;
    }
    

    【讨论】:

      【解决方案3】:

      是的,jackson 不会调用未传入有效负载的字段的 setter 方法,如果要验证缺少的字段,则需要自定义 Deserializer

      class RechargeTransferConfirmationCommandDeserializer extends JsonDeserializer<RechargeTransferConfirmationCommand> {
      
      @Override
      public RechargeTransferConfirmationCommand deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
          RechargeTransferConfirmationCommand responseModel = jp.readValueAs(RechargeTransferConfirmationCommand.class);
      
          RechargeTransferConfirmationCommand finalModel = new RechargeTransferConfirmationCommand();
          finalModel.set agency(responseModel. agency == null || age.trim().isEmpty() ? "agency" : responseModel.agency);
      
          return finalModel;
         }
      
      }
      

      你的模特应该是

      @JsonDeserialize(using = DefaultModelDeserializer.class)
      public class DefaultModel {
      
      ...
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-12
        • 2017-06-18
        • 2012-09-18
        相关资源
        最近更新 更多