Home of: [工房 "藤車"] > [SourceForge.net における SASAX]

値検証の制御

本節では、 SASAX の ValueElement(およびその派生クラス群) の値検証処理を制御する方法について説明します。

本節における話題は:

注意: 本節で説明する機能は、SASAX 1.5 以降で利用可能です。

xsi:nil に対応した解析

XML Schema 仕様によれば、 (1) XML Schema において nillable="true" が明示され、 (2) XML 文書において xsi:nil="true" が明示された場合、 そのような要素は、XML 文書において空要素でなければいけません。

XML Schema における nillable 属性記述に相当するものとして、 ValueElementsetNillable(boolean) を提供します。


IntElement element = new IntElement(parent, null, "int");
element.setNillable(true);

'xsi:nil="true"' 属性記述の許可

この操作により、 「空テキスト」は「int」の制約を満たしませんが、 上記の "element" は <int xsi:nil="true"/> を受理できるようになります。

空テキストに対する検証処理の省略

備考: 正当な XML 文書だけを受理するのであれば、 ここで説明する機能は必要ありません。

多くの開発現場においては 〜 少なくとも私の場合は 〜、 仮に「生成側」と「消費側」とで合意した XML スキーマでは許容されていないような文書であっても、 適切な箇所のみに対して処理を継続するために、 文書中の不正な箇所の無視(ないしはそれらの検出)の機能が必要とされます。

前述のように "setNillable(true)" を用いることで、 空テキストを受理可能な ValueElement を得ることが出来ます。 しかし、 それでも xsi:nil="true" 属性が付けられた要素だけに空であることが許されます。


受理される文書:

    <int xsi:nil="true"/>

受理されない文書:

    <int/>

"nillable" 要素の受理

実のところ、 空テキストが受理されるか否かは、 それぞれの型固有の検証に依存します。

例えば、 「空テキスト」は「文字列」ですから、 xsi:nil="true" 属性を持っていない要素であっても "StringElement" は空テキストを受け付けます。 その一方で、 「空テキスト」は「日時」形式の制約を満たしませんので、 "DateTimeElement" (および多くの ValueElement 派生クラス) は空テキストを受け付けません。

そのため ValueElement は、 空テキストの場合に検証処理を省略することを指定する "setIgnoreEmpty(true)" メソッドを提供しています。 例えば:


IntElement element = new IntElement(parent, null, "int");
element.setIgnoreEmpty(true);

空テキストでの検証の省略

上記コード例の elementxsi:nil="true" 属性を持っていない "<int/>" であっても受理します。

検証処理の遅延

備考: 正当な XML 文書だけを受理するのであれば、 ここで説明する機能は必要ありません。

多くの開発現場においては 〜 少なくとも私の場合は 〜、 仮に「生成側」と「消費側」とで合意した XML スキーマでは許容されていないような文書であっても、 適切な箇所のみに対して処理を継続するために、 文書中の不正な箇所の無視(ないしはそれらの検出)の機能が必要とされます。

ValueElement は "endElement" SAX イベント(= 対応するタグの終了)受理と同時に、 指定されたテキストに対する検証処理を実施します。 しかし、検証の即時性は、不正要素抜きで処理を継続することを妨げます。 例えば:


<entry name="1">
  <int>abcd</int> <!-- 不正な "int" -->
  <date>2006-12-31</date>
</entry>
<entry name="2">
  <int>1234</int>
  <date>2006/12/31</date> <!-- 不正な "date" -->
</entry>
<entry name="3">
  <int>1234</int>
  <date>2006-12-31</date>
</entry>

不正要素を含む文書

「即時検証」は name="1" エントリの int 要素の終了の際に SAXException を浮揚するため、 文書解析の継続が許されません。 そのため、 正当なエントリ(この場合は name="3" のエントリ)を処理し、 それ以外の不正なエントリに関する情報をログ出力したい、 といった状況には「即時検証」はそぐいません。

"setDelayed(true)" を実施することで、 ValueElement が 「要素の終了時に指定されたテキストを検証」することを防止します。

"setDelayed(true)" 実施により、 明示的に "validate()" を起動するまで検証処理が遅延されます。 そのため、以下のようなコードによって、 不正な "<entry>" を無視(ないし検出) することが出来ます。


CompositeElement entry = 
    new CompositeElement(parent, null, "entry");

final
IntElement intElement = 
    new IntElement(entry, null, "int");
intElement.setDelayed(true);
entry.addMustItem(intElement);

final
DateElement dateElement = 
    new DateElement(entry, null, "date", ....);
dateElement.setDelayed(true);
entry.addMustItem(dateElement);

entry.addNotification(new Notification(){
    public void elementStarted(Element element,
                               ParseContext context,
                               Attributes attributes)
    { /* nop */ }

    public void elementEnded(Element element,
                             ParseContext context)
    {
        try{
            Integer intValue = 
            (Integer)(intElement.validate(context, false));
            // 「遅延された」検証の実施

            Date dateValue =
            (Date)(dateElement.validate(context, false));
            // 「遅延された」検証の実施

            /* 固有の処理をここで実施 ..... */
        }
        catch(Exception e){
            /* 検証エラーの詳細を記録 */
        }
    }
});

不正なエントリを無視

ちなみに、 "addMustItem()" のかわりに "addOptionalItem()" を用いることで、 不完全な "<entry>" を無視 (ないしは検出)することも出来ます。


次節「パスの取り扱い」へ

詳細情報

クラス名

本チュートリアルでは、 クラスは全てクラス名のみで表記されています。 完全な名称は以下の通りです。

NotationFull name
CompositeElement jp.ne.dti.lares.foozy.sasax.CompositeElement
DateElement jp.ne.dti.lares.foozy.sasax.DateElement
Element jp.ne.dti.lares.foozy.sasax.Element
IntElement jp.ne.dti.lares.foozy.sasax.IntElement
Notification jp.ne.dti.lares.foozy.sasax.Notification
ParseContext jp.ne.dti.lares.foozy.sasax.ParseContext
StringElement jp.ne.dti.lares.foozy.sasax.StringElement
ValueElement jp.ne.dti.lares.foozy.sasax.ValueElement