要約: | - SAX は、XML パーサーおよび XML パーサー機能のための共通のインターフェイスです。
- この章では、SAX インターフェイスを使用して簡単な XML パーザーを開発する方法を説明します。
|
ODBC が、異なるリレーショナル データベースおよびそれに関連する機能として提供されているものに実装される共通インターフェイスであるように、SAX (Simple API for XML) は、異なる XML パーサーおよび XML パーサー機能に実装される汎用インターフェイスです。これは、XML-DEV メーリング リストのメンバによって共同開発されました。本ガイドおよび関連する『API リファレンス』の各セクションの説明は、「
SAX プロジェクト Web ページ (英語)」のドキュメントを土台としています。
SAX API は、Java SAX 2.0 API を Curl® 言語にマッピングしたものです。SAX 2 で使用禁止の SAX 1 クラスのサポート、あるいは SAX 2 との互換性を SAX 1 に提供するクラスのサポートは行なわれていません。クラス名は、Java SAX のクラス名と同じです。メソッド名は Curl 言語の命名規則に従います。つまり、オーバーロードされた Java™ プログラミング言語メソッドは、異なる名前で Curl 言語メソッドにマップされます。オーバーロードされた Java プログラミング言語コンストラクタは、キーワード引数でコンストラクタにマップされています。詳細は『API リファレンス』を参照してください。
Curl 言語で記述された アプレットは、パッケージ
CURL.XML.SAX.PARSER をインポートすることによって SAX API にアクセスできます。この章では、プログラム内で SAX を使用する Curl 言語開発者の方を対象に、クイック スタート チュートリアルを用意しています。
要約: | - SAX パーサー ドライバを使用するために SAXParser のインスタンスを作成します。
- set-content-handler および set-error-handler を使用してイベントハンドラを登録します。
|
{curl 8.0 applet}
{import * from CURL.XML.SAX.PARSER}
{define-class public MySAXApp {inherits DefaultHandler}
|| passed in on constructor, used for parse output
field private output:VBox
|| count element nesting for indenting parse output
field private nesting-depth:int
{constructor public {default parse-output:VBox}
set self.output = parse-output
{construct-super}
}
}
パーサーのドライバを実装する Curl 言語クラスは
SAXParser です。既定のコンストラクタを呼び出して、ドライバの新しいインスタンスを作成します。
{let xr:XMLReader = {SAXParser}}
{let xr:XMLReader = {SAXParser}}
{let parse-output:VBox = {VBox}}
{let handler:MySAXApp = {MySAXApp parse-output}}
{xr.set-content-handler handler}
{xr.set-error-handler handler}
このコードによって、XML 構文解析イベントを受け取る
MySAXApp のインスタンスを作成し、通常のコンテンツ イベントまたはエラー イベント (その他の種類のイベントもありますがほとんど使用されません) 用にそのインスタンスを XML リーダーとともに登録します。ここでは、解析出力のために
VBox を作成し、引数としてそれを
MySAXApp コンストラクタに渡します。
次に
parse-and-display と呼ばれるプロシージャを作成します。非 NULL 入力が指定されている場合、このプロシージャでは
SAXParser.parse メソッド呼び出しを実行してから出力を表示します。
解析メソッド呼び出しの周囲に try /catch ブロックをインクルードします。SAXParseException をキャッチすると解析出力 (parse output) が表示されるように、VBox にエラー メッセージ テキストを追加します。
|| This code is called when the user has selected an xml file to parse
|| The argument "input-name" is the URL of the file to parse
{define-proc {parse-and-display input-name:Url}:void
|| clear the parse output of any previous xml file
{parse-output.clear}
{try
|| Parse from what is found by opening input-name
{xr.parse {InputSource system-id = input-name.name}}
catch file-exception:IOException do
|| Handle any exception thrown due to the file read
{parse-output.add
{text color="red",
A problem occurred while reading the file:
{value file-exception}
}
}
catch parse-exception:SAXParseException do
|| Handle any exception thrown while parsing the XML
{parse-output.add
{text color="red",
A problem occurred parsing the XML file:
{error-message parse-exception}
}
}
}
}
hrule 呼び出しは、
CommandButton の下に水平線を引きます。解析出力 (parse output) は、この線の下に表示されます。
{let command:CommandButton =
{CommandButton
label = "Choose File",
{on Action do
let f:#Url = {choose-location
title="Select an XML file to parse"
}
{if-non-null f then
{parse-and-display f}
else
{parse-output.clear}
{parse-output.add
{text color="red",
Please select a file to parse.
}
}
}
}
}
}
|| Determine the location of the example XML file.
{let example-url:Url = {url "../../default/support/brillig.xml"}}
{VBox
{heading XML Example},
{text Select an XML file to be read. Hint: try
{bold {value example-url.local-filename}}
},
command,
{hrule},
{ScrollBox height=4.5in, parse-output}
}
これまでのデモ クラスの内容全体を次に示します。
{curl 8.0 applet}
{import * from CURL.XML.SAX.PARSER}
{define-class public MySAXApp {inherits DefaultHandler}
|| passed in on constructor, used for parse output
field private output:VBox
|| count element nesting for indenting parse output
field private nesting-depth:int
{constructor public {default parse-output:VBox}
set self.output = parse-output
{construct-super}
}
}
{let xr:XMLReader = {SAXParser}}
{let parse-output:VBox = {VBox}}
{let handler:MySAXApp = {MySAXApp parse-output}}
{xr.set-content-handler handler}
{xr.set-error-handler handler}
{define-proc {parse-and-display input-name:Url}:void
|| clear the parse output of any previous xml file
{parse-output.clear}
{try
|| Parse from what is found by opening input-name
{xr.parse {InputSource system-id = input-name.name}}
catch file-exception:IOException do
|| Handle any exception thrown due to the file read
{parse-output.add
{text color="red",
A problem occurred while reading the file:
{value file-exception}
}
}
catch parse-exception:SAXParseException do
|| Handle any exception thrown while parsing the XML
{parse-output.add
{text color="red",
A problem occurred parsing the XML file:
{error-message parse-exception}
}
}
}
}
{let command:CommandButton =
{CommandButton
label = "Choose File",
{on Action do
let f:#Url = {choose-location
title="Select an XML file to parse"
}
{if-non-null f then
{parse-and-display f}
else
{parse-output.clear}
{parse-output.add
{text color="red",
Please select a file to parse.
}
}
}
}
}
}
|| Determine the location of the example XML file.
{let example-url:Url = {url "../../default/support/brillig.xml"}}
{VBox
{heading XML Example},
{text Select an XML file to be read. Hint: try
{bold {value example-url.local-filename}}
},
command,
{hrule},
{ScrollBox height=4.5in, parse-output}
}
このコードをコンパイルして実行できますが、アプリケーションを設定して SAX イベントを処理していないため、存在しない URL を指定しない限り、ほとんど何も起こりません。
要約: | - start-document および end-document メソッドを使用して start-document および end-document イベントを処理します。
- start-element および end-element が要素イベントを処理します。
- 通常の文字データは、 character メソッドによって処理されます。
|
XML 構文解析イベントに応答するメソッドの実装を開始しましょう。前のセクションで、XML 構文解析イベントを受け取るクラスを登録したことを思い出してください。最も重要なイベントは、ドキュメントの開始および終了、要素の開始および終了、および文字データです。
{method public {start-document}:void
set self.nesting-depth = 0
{self.output.add
{text color="purple", Start document}
}
}
{method public {end-document}:void
{self.output.add
{text color="purple",End document}
}
}
開始 / 終了ドキュメント イベント ハンドラは引数を持ちません。SAX ドライバがドキュメントの先頭を検出すると、start-document メソッドを 1 回呼び出します。ドキュメントの終わりを検出すると end-document メソッドを 1 回呼び出します。エラーが発生すると end-document は呼び出されない可能性があります。
これらのメソッドは、コンストラクタに渡されメンバ出力に保存されている VBox に紫色のメッセージを追加します。ただし、実際のアプリケーションではこれらのハンドラに任意のコードを含めることができます。数種類のメモリ内ツリーの構築、出力の生成、データベースのポピュレート、または XML ストリームからの情報の抽出などをコードによって実行するのが最も一般的です。
{method public {start-element
uri:String,
name:String,
qname:String,
atts:Attributes
}:void
set self.nesting-depth = self.nesting-depth + 1
{self.output.add
{HBox
{Fill width = self.nesting-depth * 0.5in},
{text color="navy", Start element: },
{text color="fuchsia",
{if uri != ""
then
"[" & uri & "]"
else
""
}
},
{text color="fuchsia", {value name}}
}
}
}
{method public {end-element
uri:String,
name:String,
qname:String
}:void
{self.output.add
{HBox
{Fill width = self.nesting-depth * 0.5in},
{text color="navy", End element: },
{text color="fuchsia",
{if uri != ""
then
"[" & uri & "]"
else
""
}
},
{text color="fuchsia", {value name}}
}
}
set self.nesting-depth = self.nesting-depth - 1
}
要素が開始または終了するたびに、要素のローカル名の前にある中カッコ内の名前空間の URL を使用して、テキストを
VBox に追加します。
Fill は、要素がネストされている位置に応じてメッセージをインデントします。
start-element ごとに要素のネストの深さを増加し、
end-element ごとに減少します。
最後に、SAXParser は、characters メソッドよって通常の文字データを送信します。次の実装はすべての文字データを解析出力 (parse output) に印刷します。
{method public {characters
ch:StringBuf,
start:int,
length:int
}:void
{self.output.add
{HBox
{Fill width = self.nesting-depth * 0.5in},
{text color="teal", Characters: },
{text color="lime",
{ch.substr start, length}}
}
}
}
SAX ドライバは任意に文字データをまとめてしまうので、単一の characters イベントに到着する要素の文字データの内容をそのまま当てにすることはできません。
SAX ドライバが警告やエラーを検出すると、次の 3 つのメソッド、
error、
fatal-error、または
warning のいずれかを呼び出します。この例では
set-error-handler を呼び出すので、エラーを報告するためにこれらのメソッドを実装する必要があります。SAX ドライバは、
SAXParseException 型の引数を使用してこれらのメソッドを呼び出します。説明を簡単にするために、ここではこれらの 3 つのメソッド (
error、
fatal-error および
warning) をすべて同じ方法で実装します。例外を再スローし、解析呼び出しの周りに try-catch ブロックを置きます。
エラー、致命的エラー、および警告メソッドの実装例を次に示します。
{method public {error exception:SAXParseException}:void
{throw exception}
}
{method public {fatal-error exception:SAXParseException}:void
{throw exception}
}
{method public {warning exception:SAXParseException}:void
{throw exception}
}
最後に、表示するエラー メッセージに SAXParseException を返すために使用するプロシージャを次に示します。
{define-proc {error-message parse-exception:SAXParseException}:String
let sb:StringBuf={StringBuf "Error detected"}
{if {parse-exception.get-line-number} != 0
then
{sb.concat " at line " & {parse-exception.get-line-number}}
{if {parse-exception.get-system-id} != null
then
{sb.concat " of " & {parse-exception.get-system-id}}
}
}
{sb.concat ": " & {parse-exception.get-message}}
{return {sb.to-String}}
}
完成したサンプル アプリケーションを次に示します (実際のアプリケーションでは、イベント ハンドラを個別のクラスで実装するのが一般的です)。この短いサンプル ドキュメントでさえ 25 のイベントが生成されることに注意してください。これらのイベントには、使用される 6 つの要素の開始と終了ごとのイベント、文字データの 11 のチャンクごとのイベント、および要素間の空白 (ドキュメントの開始と終了のイベント) が含まれます。
例:
サンプル アプリケーション |
|
{import * from CURL.XML.SAX.PARSER}
{define-class public MySAXApp {inherits DefaultHandler}
field private output:VBox
field private nesting-depth:int
{constructor public {default parse-output:VBox}
set self.output = parse-output
{construct-super}
}
{method public {start-document}:void
set self.nesting-depth = 0
{self.output.add
{text color="purple", Start document}
}
}
{method public {end-document}:void
{self.output.add
{text color="purple",End document}
}
}
{method public {start-element
uri:String,
name:String,
qname:String,
atts:Attributes
}:void
set self.nesting-depth = self.nesting-depth + 1
{self.output.add
{HBox
{Fill width = self.nesting-depth * 0.5in},
{text color="navy", Start element: },
{text color="fuchsia",
{if uri != "" then
"[" & uri & "]"
else
""
}
},
{text color="fuchsia", {value name}}
}
}
}
{method public {end-element
uri:String,
name:String,
qname:String
}:void
{self.output.add
{HBox
{Fill width = self.nesting-depth * 0.5in},
{text color="navy", End element: },
{text color="fuchsia",
{if uri != "" then
"[" & uri & "]"
else
""
}
},
{text color="fuchsia", {value name}}
}
}
set self.nesting-depth = self.nesting-depth - 1
}
{method public {characters
ch:StringBuf,
start:int,
length:int
}:void
{self.output.add
{HBox
{Fill width = self.nesting-depth * 0.5in},
{text color="teal", Characters: },
{text color="lime", {ch.substr start, length}}
}
}
}
{method public {error exception:SAXParseException}:void
{throw exception}
}
{method public {fatal-error exception:SAXParseException}:void
{throw exception}
}
{method public {warning exception:SAXParseException}:void
{throw exception}
}
}
{define-proc {error-message parse-exception:SAXParseException}:String
let sb:StringBuf={StringBuf "Error detected"}
{if {parse-exception.get-line-number} != 0
then
{sb.concat " at line " & {parse-exception.get-line-number}}
{if {parse-exception.get-system-id} != null
then
{sb.concat " of " & {parse-exception.get-system-id}}
}
}
{sb.concat ": " & {parse-exception.get-message}}
{return {sb.to-String}}
}
{let xr:XMLReader = {SAXParser}}
{let parse-output:VBox = {VBox}}
{let handler:MySAXApp = {MySAXApp parse-output}}
{xr.set-content-handler handler}
{xr.set-error-handler handler}
{define-proc {parse-and-display input-name:Url}:void
|| clear the parse output of any previous xml file
{parse-output.clear}
{try
|| Parse from what is found by opening input-name
{xr.parse {InputSource system-id = input-name.name}}
catch file-exception:IOException do
|| Handle any exception thrown due to the file read
{parse-output.add
{text color="red",
A problem occurred while reading the file:
{value file-exception}
}
}
catch parse-exception:SAXParseException do
|| Handle any exception thrown while parsing the XML
{parse-output.add
{text color="red",
A problem occurred parsing the XML file:
{error-message parse-exception}
}
}
}
}
{let command:CommandButton =
{CommandButton
label = "Choose File",
{on Action do
let f:#Url = {choose-location
title="Select an XML file to parse"
}
{if-non-null f then
{parse-and-display f}
else
{parse-output.clear}
{parse-output.add
{text color="red",
Please select a file to parse.
}
}
}
}
}
}
|| Determine the location of the example XML file.
{let example-url:Url = {url "../../default/support/brillig.xml"}}
{VBox
{heading XML Example},
{text Select an XML file to be read. Hint: try
{bold {value example-url.local-filename}}
},
command,
{hrule},
{ScrollBox height=4.5in, parse-output}
}
| |
Copyright © 1998-2019 SCSK Corporation.
All rights reserved.
Curl, the Curl logo, Surge, and the Surge logo are trademarks of SCSK Corporation.
that are registered in the United States. Surge
Lab, the Surge Lab logo, and the Surge Lab Visual Layout Editor (VLE)
logo are trademarks of SCSK Corporation.