使用注释进行JAXB验证

时间:2010-03-02 18:28:57

标签: java xml validation jaxb

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

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

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

2 个答案:

答案 0 :(得分:15)

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

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

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

答案 1 :(得分:8)

很好的问题,特别是考虑到对象优先,架构永不开发的普及。我也想通过在编组之前利用现有的注释来验证对象。

虽然我们要么等待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是否处理循环图,所以在没有知道是否必须处理的情况下我没有尝试递归。我将为亲爱的读者或下一次编辑保存。