使用注释的 JAXB 验证

2022-09-01 13:21:29

如果我有一个简单的类,例如:-

@XmlRootElement
public class MyClass
{
   @XmlAttribute(required=true)
   private String myattribute
}

是否可以在没有xml架构的情况下验证相应的xml文档,即仅使用注释?


答案 1

问得好。据我所知,该属性是由XJC在找到非可选模式类型时生成的,我认为它也被模式生成器使用。然而,在运行时,它不用于任何事情,除了纪录片注释之外没有其他目的。required

您可以考虑的一件事是 JAXB 运行时的回调选项。在这种情况下,您可以只定义一个方法,该方法以编程方式验证对象的状态,如果它不喜欢它,则引发异常。有关其他选项,请参阅上面的链接,包括注册单独的验证程序类。afterUnmarshal()MyClass

话虽如此,针对架构的验证确实是最好的方法。如果你没有,你应该考虑写一个。schemagen 工具可以从对象模型生成架构,然后您可以对其进行修改以添加所需的任何约束。希望,将从类字段中生成必需的架构元素。schemagenrequired=true


答案 2

这是一个很好的问题,特别是考虑到对象优先,模式从不开发的普及。我也想在封送处理之前通过利用现有的注释来验证对象。

虽然我们要么等待JAXB-430,要么成为Java的公认贡献者,但接下来是一个非常有限的本土尝试,仅涉及。请注意,由于 ,这将不适用于非默认安全策略。XmlElement(required=true}Field.setAccessible()

用例测试

import javax.xml.bind.annotation.XmlElement;
import JaxbValidator.ValidationException;
import org.testng.annotations.Test;

public class JaxbValidatorTest {

    static class Llama {
        @XmlElement(required = false)
        private final String no;

        @XmlElement(required = true)
        private final String yes;

        public Llama(String no, String yes) {
            super();
            this.no = no;
            this.yes = yes;
        }
    }
    @Test
    public void validateRequired() {
        try {
            Llama o = new Llama("a", "b");
            // THE MAIN EVENT - see if 'required' is honored
            JaxbValidator.validateRequired(o, Llama.class);
        } catch (ValidationException e) {
            assert false : "Should not have thrown validation exception.";
        }
        try {
            Llama o = new Llama(null, null);
            // Again - see if 'required' is honored
            JaxbValidator.validateRequired(o, Llama.class);
            assert false : "Should have thrown validation exception for 'yes'";
        } catch (ValidationException e) {
            assert e.getMessage() != null: "Expected validation message, got null." ;
        }
    }
}

实现

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import javax.xml.bind.annotation.XmlElement;
import org.apache.log4j.Logger;

/**
 * oh so minimal consideration of JAXB annotation
 */
public class JaxbValidator {

    private static final Logger LOG = Logger.getLogger(JaxbValidator.class);

    public static class ValidationException extends Exception {
        public ValidationException(String message, Throwable cause) {
            super(message, cause);
        }
        public ValidationException(String message) {
            super(message);
        }
    }

    /**
     * Enforce 'required' attibute.
     *
     * Requires either no security manager is used or the default security manager is employed. 
     * @see {@link Field#setAccessible(boolean)}.
     */
    public static <T> void validateRequired(T target, Class<T> targetClass)
        throws ValidationException {
        StringBuilder errors = new StringBuilder();
        Field[] fields = targetClass.getDeclaredFields();
        for (Field field : fields) {
            XmlElement annotation = field.getAnnotation(XmlElement.class);
            if (annotation != null && annotation.required()) {
                try {
                    field.setAccessible(true);
                    if (field.get(target) == null) {
                        if (errors.length() != 0) {
                            errors.append(" ");
                        }
                        String message = String.format("%s: required field '%s' is null.",
                                                       targetClass.getSimpleName(),
                                                       field.getName());
                        LOG.error(message);
                        errors.append(message);
                    }
                } catch (IllegalArgumentException e) {
                    LOG.error(field.getName(), e);
                } catch (IllegalAccessException e) {
                    LOG.error(field.getName(), e);
                }
            }
        }
        if (errors.length() != 0) {
            throw new ValidationException(errors.toString());
        }
    }

是的...它不做深度检查。我不确定 JAXB 是否处理循环图,所以我在不知道是否必须处理的情况下没有尝试递归。我将保存它给亲爱的读者或下一次编辑。


推荐