嵌套列表对象Spring校验异常求助:NotReadablePropertyException
解决Spring Validator嵌套对象校验的NotReadablePropertyException问题
这个异常的核心原因很明确:你在BigObject的Errors上下文里直接校验SecondObject的属性,Spring会尝试在BigObject类中查找order、titol这些属性,显然找不到,所以抛出了属性不可读的异常。下面我给你两种可行的修复方案,以及指出你代码里的其他小问题。
方案一:使用嵌套路径(NestedPath)处理多层校验
Spring的Errors接口支持嵌套路径,可以让你在遍历嵌套对象时,动态切换校验的上下文路径。修改你的BigObjectValidator代码如下:
public class BigObjectValidator implements Validator { @Override public boolean supports(Class clazz) { return BigObject.class.equals(clazz); } @Override public void validate(Object obj, Errors e) { BigObject bigObject = (BigObject) obj; ValidationUtils.rejectIfEmptyOrWhitespace(e, "idObject", "empty.id"); ValidationUtils.rejectIfEmptyOrWhitespace(e, "idLanguage", "empty.id"); ValidationUtils.rejectIfEmptyOrWhitespace(e, "dateGeneration", "empty.id"); List<FirstObject> firstObjects = bigObject.getFirstObject(); if (firstObjects != null && !firstObjects.isEmpty()) { for (int i = 0; i < firstObjects.size(); i++) { FirstObject firstObject = firstObjects.get(i); // 推入FirstObject的嵌套路径 e.pushNestedPath("firstObject[" + i + "]"); if (firstObject.getSecondObject() != null && !firstObject.getSecondObject().isEmpty()) { List<SecondObject> secondObjects = firstObject.getSecondObject(); for (int j = 0; j < secondObjects.size(); j++) { SecondObject secondObject = secondObjects.get(j); // 推入SecondObject的嵌套路径 e.pushNestedPath("secondObject[" + j + "]"); validateSecondObject(secondObject, e); // 弹出当前嵌套路径,回到FirstObject的上下文 e.popNestedPath(); } } // 弹出FirstObject的嵌套路径,回到BigObject的上下文 e.popNestedPath(); } } } private void validateSecondObject(SecondObject secondObject, Errors e) { // 现在Errors的上下文是对应的secondObject,直接用属性名即可 ValidationUtils.rejectIfEmptyOrWhitespace(e, "order", "order.empty"); ValidationUtils.rejectIfEmptyOrWhitespace(e, "titol", "titol.empty"); // 注意floatProperty是int类型,不能用rejectIfEmptyOrWhitespace,要判断是否为有效范围或者非默认值 if (secondObject.getFloatProperty() <= 0) { // 假设需要大于0的有效值 e.rejectValue("floatProperty", "floatProperty.invalid"); } } }
这里的关键是pushNestedPath和popNestedPath方法,它们会动态修改Errors的属性路径前缀,比如当你推入firstObject[0].secondObject[0]后,校验order时,实际查找的路径是firstObject[0].secondObject[0].order,完全匹配嵌套对象的结构。
方案二:为每个嵌套类单独编写Validator
这种方式更符合单一职责原则,维护性更好。你可以为FirstObject和SecondObject分别创建Validator,然后在BigObjectValidator中调用它们:
1. 创建SecondObjectValidator
public class SecondObjectValidator implements Validator { @Override public boolean supports(Class<?> clazz) { return SecondObject.class.equals(clazz); } @Override public void validate(Object target, Errors errors) { SecondObject secondObject = (SecondObject) target; ValidationUtils.rejectIfEmptyOrWhitespace(errors, "order", "order.empty"); ValidationUtils.rejectIfEmptyOrWhitespace(errors, "titol", "titol.empty"); if (secondObject.getFloatProperty() <= 0) { errors.rejectValue("floatProperty", "floatProperty.invalid"); } } }
2. 创建FirstObjectValidator
public class FirstObjectValidator implements Validator { private final SecondObjectValidator secondValidator = new SecondObjectValidator(); @Override public boolean supports(Class<?> clazz) { return FirstObject.class.equals(clazz); } @Override public void validate(Object target, Errors errors) { FirstObject firstObject = (FirstObject) target; List<SecondObject> secondObjects = firstObject.getSecondObject(); if (secondObjects != null && !secondObjects.isEmpty()) { for (int j = 0; j < secondObjects.size(); j++) { errors.pushNestedPath("secondObject[" + j + "]"); secondValidator.validate(secondObjects.get(j), errors); errors.popNestedPath(); } } } }
3. 修改BigObjectValidator
public class BigObjectValidator implements Validator { private final FirstObjectValidator firstValidator = new FirstObjectValidator(); @Override public boolean supports(Class<?> clazz) { return BigObject.class.equals(clazz); } @Override public void validate(Object target, Errors errors) { BigObject bigObject = (BigObject) target; ValidationUtils.rejectIfEmptyOrWhitespace(errors, "idObject", "empty.id"); ValidationUtils.rejectIfEmptyOrWhitespace(errors, "idLanguage", "empty.id"); ValidationUtils.rejectIfEmptyOrWhitespace(errors, "dateGeneration", "empty.id"); List<FirstObject> firstObjects = bigObject.getFirstObject(); if (firstObjects != null && !firstObjects.isEmpty()) { for (int i = 0; i < firstObjects.size(); i++) { errors.pushNestedPath("firstObject[" + i + "]"); firstValidator.validate(firstObjects.get(i), errors); errors.popNestedPath(); } } } }
这种方式把每个类的校验逻辑拆分出来,代码更清晰,后续修改某一层的校验规则也更方便。
额外注意:主程序中的代码错误
你的主程序代码里有几个明显的语法/逻辑错误,也会导致问题:
List<FirstObject> firstObj = ArrayList<FirstObject>;应该是List<FirstObject> firstObj = new ArrayList<>();object.getIdObject("something");这是调用getter方法,应该是object.setIdObject(1L);(因为idObject是Long类型)object.getIdLanguage("En");同理,应该是object.setIdLanguage("En");object.getDateGeneration("05-18-2018");应该是object.setDateGeneration(new SimpleDateFormat("MM-dd-yyyy").parse("05-18-2018"));(需要处理ParseException)firstObj.set(1,secondObject);你的firstObj是空的ArrayList,索引1不存在,应该用firstObj.add(secondObject);,然后把firstObj设置给BigObject:object.setFirstObject(firstObj);
修正这些小问题后,再结合上面的校验器方案,就能正常运行了。
内容的提问来源于stack exchange,提问作者Kordh Rufan
相关产品推荐
相关产品推荐

