web-dev-qa-db-ja.com

アノテーションを使用したJAXB検証

次のような単純なクラスがある場合:-

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

Xmlスキーマなしで、つまり注釈のみを使用して、対応するxmlドキュメントを検証することは可能ですか?

28
Martin

良い質問。私の知る限り、required属性は、オプションではないスキーマタイプが見つかったときにXJCによって生成され、スキーマジェネレーターでも使用されていると思います。ただし、実行時には、ドキュメンタリーの注釈以外の目的には使用されず、何にも使用されません。

考慮できることの1つは、JAXBランタイムの コールバックオプション です。この場合、MyClassafterUnmarshal()メソッドを定義するだけで、オブジェクトの状態をプログラムで検証し、気に入らない場合は例外をスローできます。個別のバリデータークラスの登録を含む他のオプションについては、上記のリンクを参照してください。

そうは言っても、スキーマに対する検証は本当に最良の方法です。持っていない場合は、書くことを検討する必要があります。 schemagen ツールは、オブジェクトモデルからスキーマを生成できます。スキーマを変更して、必要な制約を追加できます。うまくいけば、schemagenrequired=trueクラスフィールドから必須のスキーマ要素を生成します。

15
skaffman

特にオブジェクトファースト、スキーマネバー開発の人気を考えると、すばらしい質問です。私も、マーシャリングの前に既存の注釈を活用してオブジェクトを検証したいと思います。

JAXB-4 を待つか、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が巡回グラフを処理するかどうかわからなかったので、それを処理する必要があるかどうかを知らずに再帰を試みませんでした。親愛なる読者または次の編集のためにそれを保存します。

8