如果我有一个简单的类,例如: -
@XmlRootElement
public class MyClass
{
@XmlAttribute(required=true)
private String myattribute
}
是否可以在没有xml架构的情况下验证相应的xml文档,即仅使用注释?
答案 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是否处理循环图,所以在没有知道是否必须处理的情况下我没有尝试递归。我将为亲爱的读者或下一次编辑保存。