如何强制杰克逊在控制器中调用对象(dto)中所有属性的 Set 方法?
Posted
技术标签:
【中文标题】如何强制杰克逊在控制器中调用对象(dto)中所有属性的 Set 方法?【英文标题】:How to force Jackson to invoke Set methods of all attributes in object (dto) in controller? 【发布时间】: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 验证中,您也可以使用传递验证失败消息的消息
【参考方案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
...
【讨论】:
以上是关于如何强制杰克逊在控制器中调用对象(dto)中所有属性的 Set 方法?的主要内容,如果未能解决你的问题,请参考以下文章
杰克逊 Kotlin 控制器中请求正文的序列化不与 restTemplate 一起使用,与邮递员一起使用