Отличный вопрос, особенно учитывая популярность объектно-первых, схемно-никогда не развивающихся. Я также хотел бы проверить объекты, используя существующие аннотации до маршалинга.
Пока мы либо ждем JAXB-430 , либо станем признанными участниками Java, последующая попытка сделать только XmlElement(required=true}
крайне ограничена. Обратите внимание, что это не будет работать с политикой безопасности не по умолчанию из-за Field.setAccessible()
.
Use Case Test
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 циклические графы, поэтому я не пытался выполнить рекурсию, не зная, нужно ли с этим справляться. Я оставлю это для дорогого читателя или для следующего редактирования.