スキーマについての論議のほとんどは、利用できる最良のボキャブラリや、スキーマを効率よく組織する方法 (Russian Doll (ロシア人形)、Venetian Blind (ベネチアン・ブラインド)、Salami Slice (サラミ・スライス) など) を中心としています。さらに、最適なスキーマ言語についての論議 -- DTD、W3CのXMLSchema、OASISのRelax NGのどれが最適なのか? -- も進行中です。
こうしたことは重要な考慮事項です。しかし、あなたがXMLアプリケーションを設計するときには、スキーマで何をするかということの方がさらに重要です。このヒントでは、Java API for XML Processing (JAXP) 1.2の新機能について考察しますが、これをお読みになれば、文書がスキーマに合致しているかどうか妥当性を検証する時に、より柔軟に対応できます。
一般に、アプリケーションは、エラー処理の一環として、XML文書が既知のスキーマのリストに合致しているかどうか妥当性を検証します。スキーマは、要素の名前、属性、そしてそのデータ・タイプ (integer、string、dateなど) といったボキャブラリを記述するものです。文書がスキーマに合致していると妥当性が検証される場合、アプリケーションで認識されるボキャブラリに従うことになります。結局、検証することは有用なのですが、もしアプリケーションが要素を認識しない場合には、文書を処理する上で何が大切なのでしょうか?
まだ有用なものではないのですが、既知のスキーマにアプリケーションが合致しているかどうか妥当性の検証を行うことが重要です。ただ、この検証は、JAXP
1.2が登場するまでは「言うは易し行うは難し」という代物でした。たとえば、文書を手軽にスキーマと関連付けるときにはどうしますか。ほとんどの場合、
xsi:schemaLocation
属性を使うことになります。この属性は、名前空間URIと関連付けられたスキーマ・ファイルと組み合わせて使用します
(名前空間のない文書では、
xsi:noNamespaceSchemaLocation
属性を使用します)。リスト1では、
schemaLocation
属性が、
http://ananas.org/2003/tips/validate
名前空間を、ファイル
simple.xsd
に関連付けています。
リスト1. xsi:schemaLocation属性を使うXML文書
<?xml version="1.0"?>
<simple:Root
xmlns:simple="http://ananas.org/2003/tips/validate"
xsi:schemaLocation="http://ananas.org/2003/tips/validate simple.xsd">
Document content comes here.
</simple:Root>
|
この属性は、XMLスキーマを管理するための簡単な解決策になりますが、重大な欠陥が1つあります。つまり、ご使用のアプリケーションが
xsi:schemaLocation
属性を制御できると見なされてしまうのです。アプリケーションによっては、これが当てはまる場合もありますし、当てはまらない場合もあります。以下のシナリオを考えてみてください。
-
Corel XMetaLやXMLmind XML
EditorなどのXMLエディターによって、適切なファイルを指すよう
xsi:schemaLocation属性を変更することが可能である。 -
XMやCocoonのようなWeb公開フレームワーク側では、作成者が
xsi:schemaLocationを正確に設定しているものと想定するが、複数の作成者がサイトに関わった場合には正確に設定されていないものが存在する場合がある。 - 着信XML文書を扱う電子商取引サーバーはそれらの文書の妥当性を検証する必要があるが、他のパーティーがスキーマを正しく設定したということを信頼できない場合がある。
このようなシナリオに示されているように、
xsi:schemaLocation
属性は、小規模なアプリケーションでは十分に有用ですが、分散が進むと管理が著しく難しくなります。とりわけ、スキーマが別のコンピュータでも同じ名前で保管されるといった可能性は低いものです。
当然、文書が正しくないという危険性が存在する場合に、アプリケーションはその妥当性を検証します。そのような文書が、
xsi:schemaLocation
などの内容によって変わるのであれば、妥当性の検証を堅牢にすることはできません。いったい、文書の残りの部分よりも属性のほうが正しいということがあり得るでしょうか。明らかに、別の解決策、すなわち、より一層の制御権をアプリケーションに委ねる方策が必要です。
スキーマ仕様では、
xsi:schemaLocation
に起因する堅牢性の不足が正しく認識されています。仕様によると、パーサにとっては
xsi:schemaLocation
はヒントに過ぎないもので、パーサは他の方法を使用して適用するスキーマを決定できます。残念ながら、仕様では他の方法については教えてくれません。JAXPのメンテナンス・リリースであるJAXP
1.2がこの穴を埋めてくれます。これには、Javaプラットフォームでの標準メカニズムが備えられています。
基本的に、JAXP 1.2は2つの新しいプロパティ (SAXパーサ用)
と、スキーマの妥当性検証を制御する2つの新しい属性 (DOMパーサ用) を定義します。最初のプロパティー (
http://java.sun.com/xml/jaxp/properties/schemaLanguage
) は、使用するスキーマ言語を指定します。今のところ、受け入れられる値は
http://www.w3.org/2001/XMLSchema
(XMLスキーマに関するW3C勧告)だけです。将来のリリースでは、Relax
NGや他のスキーマ言語向けに、別の値がサポートされる予定です。
2番目のプロパティ (
http://java.sun.com/xml/jaxp/properties/schemaSource
) は、スキーマの場所を設定します。これは非常に興味深い点の1つです。以下のような、さまざまな値を使用できます。
- スキーマのURIを含む文字列。
-
スキーマの内容を含む
InputStreamオブジェクト。 -
スキーマを指す
InputSourceオブジェクト。 -
スキーマ・ファイルを指す
Fileオブジェクト。 - このような定義済みタイプのいずれかを含む配列。この配列は、異なるスキーマに適合できる文書をアプリケーションが受け入れる場合に役立ちます。
リスト2 には、JAXP 1.2で新しいプロパティを使用して、SAXパーサで文書の妥当性を検証する方法が示されています。文書の妥当性を検証するためにSAXパーサを使用するときには、以下のようにします。
-
SAXParserFactoryオブジェクトを作成します。 - 名前空間認識プロパティと妥当性検証プロパティをtrueに設定します。
-
SAXParserオブジェクトを取得します。 - スキーマ言語とスキーマ・ソースのプロパティを設定します (これは、JAXP 1.2とスキーマで新しく導入された部分です)。
-
文書を構文解析します。パーサには、
ErrorHandlerオブジェクトへのアクセス権が必要です。
リスト2. JAXP 1.2の説明
package org.ananas.tips;
import java.io.*;
import org.xml.sax.*;
import javax.xml.parsers.*;
public class ValidateSAX
{
public static String SCHEMA_LANGUAGE =
"http://java.sun.com/xml/jaxp/properties/schemaLanguage",
XML_SCHEMA =
"http://www.w3.org/2001/XMLSchema",
SCHEMA_SOURCE =
"http://java.sun.com/xml/jaxp/properties/schemaSource";
public final static void main(String[] args)
throws IOException, SAXException, ParserConfigurationException
{
if(args.length < 2)
{
System.err.println("usage is:");
System.err.println(" java -jar tips.jar -validatesax "
+ "input.xml schema.xsd");
return;
}
File input = new File(args[0]),
schema = new File(args[1]);
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(true);
SAXParser parser = factory.newSAXParser();
try
{
parser.setProperty(SCHEMA_LANGUAGE,XML_SCHEMA);
parser.setProperty(SCHEMA_SOURCE,schema);
}
catch(SAXNotRecognizedException x)
{
System.err.println("Your SAX parser is not JAXP 1.2 compliant.");
}
parser.parse(input,new ErrorPrinter());
}
}
|
リスト2
をテストするには、JAXP
1.2準拠パーサが必要です。お気に入りのパーサの資料を調べるか、最新バージョンのApache
Xercesをダウンロードしてください
(私は、このヒントを準備するにあたってバージョン2.4.0を使用しました)。パーサがJAXP
1.2準拠ではない場合、プロパティを設定しようとすると、
SAXNotRecognizedException
例外がスローされます。これは、最新バージョンのXercesにアップグレードしなさいという合図です。
リスト2
の場合は、リスト3に示されているように、コンソールに妥当性の検証エラーを表示する
DefaultHandler
オブジェクトを登録するだけです。あなたのアプリケーションでは、文書の内容に何らかの処理を行うような、さらに面白いハンドラーを登録できます。
リスト3. ErrorPrinter.java
package org.ananas.tips;
import java.text.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
public class ErrorPrinter
extends DefaultHandler
{
private MessageFormat message =
new MessageFormat("({0}: {1}, {2}): {3}");
private void print(SAXParseException x)
{
String msg = message.format(new Object[]
{
x.getSystemId(),
new Integer(x.getLineNumber()),
new Integer(x.getColumnNumber()),
x.getMessage()
});
System.out.println(msg);
}
public void warning(SAXParseException x)
{
print(x);
}
public void error(SAXParseException x)
{
print(x);
}
public void fatalError(SAXParseException x)
throws SAXParseException
{
print(x);
throw x;
}
}
|
JAXP 1.2では、 リスト4 に示されているように、DOMパーサのスキーマ・サポートも定義されています。その手順はSAXパーサの手順と非常に似ていて、相違点は、パーサ・オブジェクトにプロパティを設定する代わりに、ファクトリ・オブジェクトに属性を設定することだけです。詳しい手順は、以下のとおりです。
-
DOMBuilderFactoryオブジェクトを作成します。 -
名前空間認識プロパティ妥当性検証プロパティを
trueに設定します。 -
スキーマ言語とスキーマ・ソースの属性を設定します。パーサがJAXP 1.2準拠ではない場合、
IllegalArgumentException例外がスローされます。 -
DocumentBuilderオブジェクト (パーサ) を取得します。 -
ErrorHandlerオブジェクトをパーサに登録します。 - 文書を構文解析します。
この例は、検査を行うだけのものです。あなたのアプリケーションでは、解析ツリーを使用してさらに面白いことができます。
リスト4. ValidateDOM.java
package org.ananas.tips;
import java.io.*;
import org.xml.sax.*;
import javax.xml.parsers.*;
public class ValidateDOM
{
public static String SCHEMA_LANGUAGE =
"http://java.sun.com/xml/jaxp/properties/schemaLanguage",
XML_SCHEMA =
"http://www.w3.org/2001/XMLSchema",
SCHEMA_SOURCE =
"http://java.sun.com/xml/jaxp/properties/schemaSource";
public final static void main(String[] args)
throws IOException, SAXException, ParserConfigurationException
{
if(args.length < 2)
{
System.err.println("usage is:");
System.err.println(" java -jar tips.jar -validatedom "
+ "input.xml schema.xsd");
return;
}
File input = new File(args[0]),
schema = new File(args[1]);
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(true);
try
{
factory.setAttribute(SCHEMA_LANGUAGE,XML_SCHEMA);
factory.setAttribute(SCHEMA_SOURCE,schema);
}
catch(IllegalArgumentException x)
{
System.err.println("Your DOM parser is not JAXP 1.2 compliant.");
}
DocumentBuilder parser = factory.newDocumentBuilder();
parser.setErrorHandler(new ErrorPrinter());
parser.parse(input);
}
}
|
XMLスキーマを利用して、堅牢な妥当性の検証を実装する際には、あなたのアプリケーションが文書妥当性を検証するときに、(当然のことですが)
それらの文書が正しいという思い込みを持ってはならないことを念頭においてください。さらに言うならば、文書が適切な
xsi:schemaLocation
属性を持っていると思い込まないようにしてください。
-
この記事で使用された
ソース・コード
をダウンロードしてください。さらに、最新バージョンの
Apache Xerces
か、別のJAXP 1.2準拠のパーサも必要です。
-
XMLmind XML Editor
と
Corel XMetaL
は、スキーマをサポートする代表的なXMLエディターです。
XM
と
Cocoon
は、代表的な公開ソリューションです。作成者が多すぎない場合には、これらのアプリケーションで
xsi:schemaLocationに頼ることができます。 -
RELAX NG
は、XMLSchemaの代わりになるものです。
-
Roger Costello氏の著作『
XML Schemas: Best Practices
』では、Venetian Blind (ベネチアン・ブラインド) やRussian Doll
(ロシア人形) といったエキゾチックな名前で、様々な設計テクニックについて論じられています。
-
XMLおよび関連テクノロジーのIBM認証開発者
になる方法についてはこちらを参照してください。

Benoit Marchal氏は、ベルギーのナミュールを拠点にしたコンサルタントおよび著述家です。彼の著作には、 XML by Example(Que社、邦訳: インプレス社「実例で学ぶXML」。間もなく第2版が出版される予定です)、 Applied XML Solutions および XML and the Enterprise があります。また、Gamelanのコラムや、developerWorks XML zoneのコラムWorking XML の著者でもあります。最新プロジェクトの詳細については、www.marchal.com をご覧ください。