Подтвердить что ты не робот

Проверка JAXB с помощью аннотаций

Если у меня есть простой класс, например: -

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

Можно ли проверить соответствующий XML-документ БЕЗ xml-схемы, то есть использовать только аннотации?

4b9b3361

Ответ 1

Хороший вопрос. Насколько мне известно, атрибут required генерируется XJC, когда он находит необязательный тип схемы, и я думаю, что он также используется генератором схемы. Однако во время выполнения он не использовался ни для чего, не предназначенный для других целей, кроме документальной аннотации.

Одна вещь, которую вы могли бы рассмотреть, - это параметры обратного вызова schemagen может генерировать схему из вашей объектной модели, которую вы затем можете изменить, чтобы добавить любые ограничения, которые вам нравятся. Надеемся, что schemagen будет генерировать обязательные элементы схемы из полей класса required=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 обрабатывает циклические графики, поэтому я не пытался рекурсии, не зная, с этим нужно бороться. Я сохраню это для дорогого читателя или следующего редактирования.