この節で説明される関数および擬似関数式は、xml型の値に対して機能します。
xml型についての情報は8.13を点検してください。
xml型のやりとりを変換するxmlparseおよびxmlserialize擬似関数式はここでは繰り返しません。
これらの多くの関数を使用するには、インストレーションの際configure --with-libxml付きでビルドされていることが必要です。
SQLデータからXML内容を生成するために関数と擬似関数式の一式が提供されています。 そのようなものとして、クライアントアプリケーションが問い合わせ結果を処理のためXML文書に書式化するのにこれらは特に適しています。
xmlcommentxmlcomment(text)
関数xmlcommentは指定のテキストを内容とするXMLコメントを含んだXML値を作成します。
結果として構築されるXMLコメントが有効になるよう、テキストは「--」を含むこと、または「-」で終結することはできません。
引数がNULLならば結果もNULLになります。
例:
SELECT xmlcomment('hello');
xmlcomment
--------------
<!--hello-->
xmlconcatxmlconcat(xml[, ...])
関数xmlconcatは、個々のXML値のリストを結合し、XMLの内容断片を含む単一の値を作成します。
NULL値は削除され、NULL以外の引数が存在しないときのみ結果はNULLになります。
例:
SELECT xmlconcat('<abc/>', '<bar>foo</bar>');
xmlconcat
----------------------
<abc/><bar>foo</bar>
XML宣言が提示されている場合は次のように組み合わされます。 全ての引数の値が同一のXMLversion宣言を持っていれば、そのversionが結果に使用されます。さもなければversionは使用されません。 全ての引数の値でstandaloneの宣言値が「yes」であれば、その値が結果に使用されます。 全ての引数の値にstandalone宣言値があり、その中で1つでも「no」がある場合、それが結果に使用されます。 それ以外の場合は、結果はstandalone宣言を持ちません。 standalone宣言を必要とするが、standalone宣言がないという結果になった場合には、version 1.0のversion宣言が使用されます。 これはXMLがXML宣言においてversion宣言を含むことを要求するためです。 encoding宣言は無視され、全ての場合で削除されます。
例:
SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone="no"?><bar/>');
xmlconcat
-----------------------------------
<?xml version="1.1"?><foo/><bar/>
xmlelementxmlelement(namename[, xmlattributes(value[ASattname] [, ... ])] [, content, ...])
xmlelement式は与えられた名前、属性、および内容を持つXML要素を生成します。
例:
SELECT xmlelement(name foo);
xmlelement
------------
<foo/>
SELECT xmlelement(name foo, xmlattributes('xyz' as bar));
xmlelement
------------------
<foo bar="xyz"/>
SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent');
xmlelement
-------------------------------------
<foo bar="2007-01-26">content</foo>
有効なXML名ではない要素名と属性名は、シーケンス_xにより障害となる文字を置換することでエスケープされます。ここで、HHHH_HHHHは16進数によるその文字のUnicode文字コード番号です。
例をあげます。
SELECT xmlelement(name "foo$bar", xmlattributes('xyz' as "a&b"));
xmlelement
----------------------------------
<foo_x0024_bar a_x0026_b="xyz"/>
属性値が列参照の場合、明示的な属性名を指定する必要はありません。この場合、デフォルトで列名が属性名として使用されます。 その他の場合には、属性は明示的な名前で与えられなければなりません。 従って、以下の例は有効です。
CREATE TABLE test (a xml, b xml); SELECT xmlelement(name test, xmlattributes(a, b)) FROM test;
しかし、以下の例は有効ではありません。
SELECT xmlelement(name test, xmlattributes('constant'), a, b) FROM test;
SELECT xmlelement(name test, xmlattributes(func(a, b))) FROM test;
もし要素内容が指定されればそのデータ型に従って書式化されます。
もし内容そのものがxml型であれば、複合XML文書が構築されます。
例をあげます。
SELECT xmlelement(name foo, xmlattributes('xyz' as bar),
xmlelement(name abc),
xmlcomment('test'),
xmlelement(name xyz));
xmlelement
----------------------------------------------
<foo bar="xyz"><abc/><!--test--><xyz/></foo>
そのほかの型の内容は有効なXML文字データにフォーマットされます。
これは特に文字<、>、および&がエンティティに変換されることを意味します。
バイナリデータ(データ型はbytea)は、設定パラメータxmlbinaryの設定にしたがって、base64もしくは16進符号化方式で表現されます。
個々のデータ型に対する特定の動作は、XMLスキーマ仕様でのSQLおよびPostgreSQLデータ型に調整するため発展すると期待されます。
その時点で記述がより詳細になるでしょう。
xmlforestxmlforest(content[ASname] [, ...])
xmlforest式は与えられた名前と内容を使用し、要素のXMLフォレスト(シーケンス)を生成します。
例:
SELECT xmlforest('abc' AS foo, 123 AS bar);
xmlforest
------------------------------
<foo>abc</foo><bar>123</bar>
SELECT xmlforest(table_name, column_name)
FROM information_schema.columns
WHERE table_schema = 'pg_catalog';
xmlforest
-------------------------------------------------------------------------------------------
<table_name>pg_authid</table_name><column_name>rolname</column_name>
<table_name>pg_authid</table_name><column_name>rolsuper</column_name>
...
第2の例に見られるように、内容の値が列参照の場合、要素名は省略可能です。この時は、列名がデフォルトで使用されます。 そうでない時は、名前が指定されなければなりません。
有効なXML名ではない要素名は上のxmlelementで説明した通りエスケープされます。
同様にして、既にxml型であるものを除き、内容データは有効なXML内容になるようにエスケープされます。
XMLフォレストは2つ以上の要素からなる場合、有効なXML文書ではないことに注意してください。
したがって、xmlelement内にxmlforest式をラップすることが有用なことがあります。
xmlpixmlpi(nametarget[,content])
xmlpi式はXML処理命令を作成します。
内容が存在すれば、その内容は?>文字シーケンスを含んではなりません。
例:
SELECT xmlpi(name php, 'echo "hello world";');
xmlpi
-----------------------------
<?php echo "hello world";?>
xmlrootxmlroot(xml, versiontext| no value [, standalone yes|no|no value])
xmlroot式はXML値のルートノードの属性を変更します。
versionが指定されていると、ルートノードのversion宣言での値を変更し、standalone設定が指定されていると、ルートノードのstandalone宣言での値を変更します。
SELECT xmlroot(xmlparse(document '<?xml version="1.1"?><content>abc</content>'),
version '1.0', standalone yes);
xmlroot
----------------------------------------
<?xml version="1.0" standalone="yes"?>
<content>abc</content>
xmlaggxmlagg(xml)
ここで説明している他の関数とは異なり、xmlagg関数は集約関数です。
これはxmlconcatが行うように、入力値を連結する集約関数ですが、単一行内の複数の式にまたがった連結ではなく、複数行にまたがった連結を行います。
集約関数についての追加情報は9.20を参照してください。
例:
CREATE TABLE test (y int, x xml);
INSERT INTO test VALUES (1, '<foo>abc</foo>');
INSERT INTO test VALUES (2, '<bar/>');
SELECT xmlagg(x) FROM test;
xmlagg
----------------------
<foo>abc</foo><bar/>
連結の順序を決定するため、4.2.7に記述されているようにORDER BY句を集計呼び出しに追加することができます。
以下は例です。
SELECT xmlagg(x ORDER BY y DESC) FROM test;
xmlagg
----------------------
<bar/><foo>abc</foo>
下記は以前のバージョンで推奨されていた、非標準的な方法例です。特定のケースでは有用かもしれません。
SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
xmlagg
----------------------
<bar/><foo>abc</foo>
この節で記述されている式は、xml値の属性をチェックします。
IS DOCUMENT
xml IS DOCUMENT
式IS DOCUMENTは引数XML値が適切なXML文書であれば真を返し、そうでなければ(つまり、内容の断片)偽を返すか、もしくは引数がNULLであればNULLを返します。
文書と内容の断片の差異については8.13を参照してください。
IS NOT DOCUMENT
xml IS NOT DOCUMENT
式IS NOT DOCUMENTは引数XML値が適切なXML文書であれば偽を返し、そうでなければ(つまり、内容の断片)真を返すか、もしくは引数がNULLであればNULLを返します。
XMLEXISTSXMLEXISTS(textPASSING [BY REF]xml[BY REF])
関数xmlexistsは第一引数のXPath式が何かしらのノードであれば真を返し、そうでなければ偽を返します。
(もしいずれの引数もNULLであった場合はNULLを返します。)
例:
SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF '<towns><town>Toronto</town><town>Ottawa</town></towns>');
xmlexists
------------
t
(1 row)
BY REF句は、PostgreSQLには何の影響も与えませんが、他の実装とのSQL互換性や順応性のため、付与することができます。
SQL標準では1つ目のBY REFを必要としており、2つ目はオプショナルです。
加えてSQL標準ではxmlexistsはXQuery式を第一引数として取る構成としていますが、PostgreSQLでは現在XQueryのサブセットにあたるXPathのみサポートしていることに注意してください。
xml_is_well_formedxml_is_well_formed(text)xml_is_well_formed_document(text)xml_is_well_formed_content(text)
これらの関数はtext文字列が整形式かどうかをチェックし、論理値で結果を返します。
xml_is_well_formed_documentは文書が整形式かをチェックし、一方xml_is_well_formed_contentは内容が整形式かをチェックします。
xml_is_well_formedは、xmloptionパラメータ値がDOCUMENTに設定されていれば前者を、CONTENTが設定されていれば後者のチェックを実施します。
これは、xml_is_well_formedは単純なxml型へのキャストが成功するかの判断に有用であり、その他の2つの関数はXMLPARSEの対応による変換が成功するかの判断に有用であることを意味します。
例:
SET xmloption TO DOCUMENT;
SELECT xml_is_well_formed('<>');
xml_is_well_formed
--------------------
f
(1 row)
SELECT xml_is_well_formed('<abc/>');
xml_is_well_formed
--------------------
t
(1 row)
SET xmloption TO CONTENT;
SELECT xml_is_well_formed('abc');
xml_is_well_formed
--------------------
t
(1 row)
SELECT xml_is_well_formed_document('<pg:foo xmlns:pg="http://postgresql.org/stuff">bar</pg:foo>');
xml_is_well_formed_document
-----------------------------
t
(1 row)
SELECT xml_is_well_formed_document('<pg:foo xmlns:pg="http://postgresql.org/stuff">bar</my:foo>');
xml_is_well_formed_document
-----------------------------
f
(1 row)
最後の例は、名前空間が正しく一致しているかのチェックも含むことを示しています。
データ型xmlの値を処理するため、PostgreSQLはXPath 1.0式を評価する関数xpathおよびxpath_existsと、テーブル関数XMLTABLEを提供しています。
xpathxpath(xpath,xml[,nsarray])
関数xpathは、XML値xmlに対し、XPath式xpath(ひとつのtext値)を評価します。そして、XPath式で作成されたノードセットに対応するXML値の配列を返します。
もし、XPath式がノードセットではなくスカラー値を返す場合、単一要素の配列が返されます。
2番目の引数は整形済XML文書でなければなりません。特に、単一のルートノード要素を持たなければなりません。
オプショナルな関数の3番目の引数は名前空間マッピング配列です。
この配列は、第2軸が2に等しい長さをもつ2次元text配列です(つまり、それは配列の配列で、それぞれは正確に2つの要素からなります)。
それぞれの配列のエントリの最初の要素は名前空間の名前(別名)で、2番目は名前空間のURIです。
この配列内で提供される別名がXML文書自身で使用されるものと同じであることは必要ではありません(言い換えると、XML文書内およびxpath関数の両方の文脈の中で、別名はローカルです)。
例:
SELECT xpath('/my:a/text()', '<my:a xmlns:my="http://example.com">test</my:a>',
ARRAY[ARRAY['my', 'http://example.com']]);
xpath
--------
{test}
(1 row)
デフォルト(匿名)名前空間を取り扱うためには、以下のようなことを実施してください。
SELECT xpath('//mydefns:b/text()', '<a xmlns="http://example.com"><b>test</b></a>',
ARRAY[ARRAY['mydefns', 'http://example.com']]);
xpath
--------
{test}
(1 row)
xpath_existsxpath_exists(xpath,xml[,nsarray])
関数xpath_existsは、xpath関数の特別な形式です。この関数は、XPathを満足する個別のXML値を返す代わりに、問い合わせがそれを満足するかどうかを論理値で返します。
この関数は、名前空間にマッピングされた引数をもサポートする点を除き、標準のXMLEXISTS述語と同じです。
例:
SELECT xpath_exists('/my:a/text()', '<my:a xmlns:my="http://example.com">test</my:a>',
ARRAY[ARRAY['my', 'http://example.com']]);
xpath_exists
--------------
t
(1 row)
xmltablexmltable( [XMLNAMESPACES(namespace uriASnamespace name[, ...]), ]row_expressionPASSING [BY REF]document_expression[BY REF] COLUMNSname{type[PATHcolumn_expression] [DEFAULTdefault_expression] [NOT NULL | NULL] | FOR ORDINALITY } [, ...] )
xmltable関数は、与えられたXML値、行を抽出するXPathフィルタ、オプションの列定義の集合に基づいてテーブルを生成します。
オプションのXMLNAMESPACES句はカンマで区切られた名前空間のリストです。
これは文書とその別名で使用されるXML名前空間を指定します。
デフォルトの名前空間指定は現在のところサポートされていません。
必須のrow_expression引数はXPath式で、指定のXML文書に対して評価され、XMLノードの順序付きシーケンスが取得されます。
このシーケンスがxmltableにより出力行に変換されます。
document_expressionは演算の対象となるXML文書を提供します。
BY REF句はPostgreSQLでは何の効果もありませんが、SQL準拠および他の実装との互換性のために受け入れられます。
引数は整形されたXMLドキュメントでなければならず、フラグメントやフォレストは受け付けられません。
必須のCOLUMNS句は、出力テーブルの列のリストを指定します。
COLUMNS句を省略した場合、結果集合の行にはxml型の列が1つだけ含まれ、そこにはrow_expressionにマッチしたデータが含まれます。
COLUMNSが指定された場合、各エントリは一つの列を表します。
形式については上記の構文サマリーを参照してください。
列名と型は必須ですが、パス、デフォルト値、NULLを許すかどうかの句は省略できます。
FOR ORDINALITYと印がつけられた列には、元の入力XMLドキュメントの中に現れた出力行の順序に対応する行番号が入ります。
FOR ORDINALITYの印が付けられるのは最大でも1列です。
列のcolumn_expressionはXPath式で、row_expressionの結果に対応する各行について評価されて、列の値を得ます。
column_expressionが与えられなかった場合は、暗示的なパスとして列名が使用されます。
列のXPath式が複数の要素を戻した場合、エラーが発生します。
式が空のタグとマッチした場合、結果は空文字列となります(NULLではありません)。
xsi:nilの属性はすべて無視されます。
column_expressionにマッチしたXMLのテキスト本体が列の値として使用されます。
要素内の複数のtext()ノードは順番に結合されます。
子要素、処理命令、コメントはすべて無視されますが、子要素のテキストコンテンツは結果に結合されます。
2つの非テキスト要素間にある空白文字のみのtext()ノードは保存されること、またtext()ノードの先頭にある空白文字は削られないことに注意してください。
パス式が行とマッチせず、default_expressionが指定されている場合は、その式を評価した結果の値が使用されます。
その列にDEFAULT句が指定されていない場合は、そのフィールドはNULLに設定されます。
default_expressionは列リスト内でそれより前に現れる出力列の値を参照して、ある列のデフォルト値を他の列の値に基づくものにすることができます。
列にはNOT NULLの印をつけることができます。
NOT NULLの列のcolumn_expressionが何にもマッチせず、DEFAULTがない、あるいはdefault_expressionの評価結果もNULLになるという場合はエラーが報告されます。
PostgreSQLの通常の関数とは異なり、column_expressionとdefault_expressionは関数を呼び出す前には単純な値に評価されません。
column_expressionは通常は一つの入力行に対してちょうど一度だけ評価され、default_expressionはフィールドにデフォルト値が必要になる度に評価されます。
式が安定(stable)または不変(immutable)とみなされる場合、評価は繰り返し行われないかもしれません。
実際上、xmltableは関数呼び出しとしてよりも、副問合せのように動作します。
これはdefault_expressionの中でnextvalのような揮発性(volatile)の関数を有効に使用できること、またcolumn_expressionはXML文書の他の部分に依存するかもしれないということを意味します。
例:
CREATE TABLE xmldata AS SELECT
xml $$
<ROWS>
<ROW id="1">
<COUNTRY_ID>AU</COUNTRY_ID>
<COUNTRY_NAME>Australia</COUNTRY_NAME>
</ROW>
<ROW id="5">
<COUNTRY_ID>JP</COUNTRY_ID>
<COUNTRY_NAME>Japan</COUNTRY_NAME>
<PREMIER_NAME>Shinzo Abe</PREMIER_NAME>
<SIZE unit="sq_mi">145935</SIZE>
</ROW>
<ROW id="6">
<COUNTRY_ID>SG</COUNTRY_ID>
<COUNTRY_NAME>Singapore</COUNTRY_NAME>
<SIZE unit="sq_km">697</SIZE>
</ROW>
</ROWS>
$$ AS data;
SELECT xmltable.*
FROM xmldata,
XMLTABLE('//ROWS/ROW'
PASSING data
COLUMNS id int PATH '@id',
ordinality FOR ORDINALITY,
"COUNTRY_NAME" text,
country_id text PATH 'COUNTRY_ID',
size_sq_km float PATH 'SIZE[@unit = "sq_km"]',
size_other text PATH
'concat(SIZE[@unit!="sq_km"], " ", SIZE[@unit!="sq_km"]/@unit)',
premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified') ;
id | ordinality | COUNTRY_NAME | country_id | size_sq_km | size_other | premier_name
----+------------+--------------+------------+------------+--------------+---------------
1 | 1 | Australia | AU | | | not specified
5 | 2 | Japan | JP | | 145935 sq_mi | Shinzo Abe
6 | 3 | Singapore | SG | 697 | | not specified
以下の例では、複数のtext()ノードの結合、列名のXPathフィルターとしての使用、空白文字、XMLコメント、処理命令の取扱いを示します。
CREATE TABLE xmlelements AS SELECT
xml $$
<root>
<element> Hello<!-- xyxxz -->2a2<?aaaaa?> <!--x--> bbb<x>xxx</x>CC </element>
</root>
$$ AS data;
SELECT xmltable.*
FROM xmlelements, XMLTABLE('/root' PASSING data COLUMNS element text);
element
----------------------
Hello2a2 bbbCC
以下の例では、XMLNAMESPACES句を使ってXMLドキュメントやXPath式で使われる追加の名前空間のリストを指定する方法を示します。
WITH xmldata(data) AS (VALUES ('
<example xmlns="http://example.com/myns" xmlns:B="http://example.com/b">
<item foo="1" B:bar="2"/>
<item foo="3" B:bar="4"/>
<item foo="4" B:bar="5"/>
</example>'::xml)
)
SELECT xmltable.*
FROM XMLTABLE(XMLNAMESPACES('http://example.com/myns' AS x,
'http://example.com/b' AS "B"),
'/x:example/x:item'
PASSING (SELECT data FROM xmldata)
COLUMNS foo int PATH '@foo',
bar int PATH '@B:bar');
foo | bar
-----+-----
1 | 2
3 | 4
4 | 5
(3 rows)
以下の関数はリレーショナルテーブルの内容をXML値にマップします。 これらはXMLエクスポート機能と考えることができます。
table_to_xml(tbl regclass, nulls boolean, tableforest boolean, targetns text)
query_to_xml(query text, nulls boolean, tableforest boolean, targetns text)
cursor_to_xml(cursor refcursor, count int, nulls boolean,
tableforest boolean, targetns text)
それぞれの関数の戻り値型はxmlです。
table_to_xmlは、パラメータtblとして渡された名前付きのテーブルの内容をマップします。
regclass型はオプションのスキーマ修飾と二重引用符を含む、通常の表記法を使用しテーブルを特定する文字列を受け付けます。
query_to_xmlは、パラメータqueryとしてテキストが渡された問い合わせを実行し、結果セットをマップします。
cursor_to_xmlは、パラメータcursorで指定されたカーソルから提示された行数を取得します。
それぞれの関数により結果値がメモリーに構築されるため、この異形は巨大なテーブルをマップする必要がある場合推奨されます。
tableforestが偽であれば、結果のXML文書は以下のようになります。
<tablename>
<row>
<columnname1>data</columnname1>
<columnname2>data</columnname2>
</row>
<row>
...
</row>
...
</tablename>
tableforestが真であれば、結果は以下のようなXML文書の断片です。
<tablename> <columnname1>data</columnname1> <columnname2>data</columnname2> </tablename> <tablename> ... </tablename> ...
テーブル名が利用できないとき、つまり、問い合わせ、またはカーソルをマップする時は、最初の書式では文字列tableが使用され、2番目の書式ではrowが使用されます。
これらどの書式を選択するのかはユーザ次第です。
最初の書式は適切なXML文書で、多くのアプリケーションにおいて重要です。
第2の書式は、後に結果値が1つの文書に再び組み立てられる場合、cursor_to_xml関数内でより有用になる傾向があります。
上記で説明したXML内容を作成する関数、特にxmlelementは結果を好みにかえるために使用することができます。
データの値は上記関数xmlelementで説明したのと同じ方法でマップされます。
パラメータnullsは出力にNULL値が含まれる必要があるかを決定します。
もし真であれば列内のNULL値は以下のように表現されます。
<columnname xsi:nil="true"/>
ここでxsiはXMLスキーマインスタンスに対するXML名前空間接頭辞です。
適切な名前空間宣言が結果値に追加されます。
もし偽の場合、NULL値を含む列は単に出力から削除されます。
パラメータtargetnsは結果の希望するXML名前空間を指定します。
特定の名前空間が必要なければ、空文字列を渡す必要があります。
以下の関数は、対応する上記関数により行われたマッピングを記述するXMLスキーマ文書を返します。
table_to_xmlschema(tbl regclass, nulls boolean, tableforest boolean, targetns text) query_to_xmlschema(query text, nulls boolean, tableforest boolean, targetns text) cursor_to_xmlschema(cursor refcursor, nulls boolean, tableforest boolean, targetns text)
一致するXMLデータマッピングとXMLスキーマ文書を取得するため、同じパラメータが渡されることが不可欠です。
以下の関数は、XMLデータマッピングとそれに対応するXMLスキーマがお互いにリンクされた、1つの文書(またはフォレスト)を作成します。 これらは自己完結した、自己記述的な結果を希望する場合に便利です。
table_to_xml_and_xmlschema(tbl regclass, nulls boolean, tableforest boolean, targetns text) query_to_xml_and_xmlschema(query text, nulls boolean, tableforest boolean, targetns text)
さらに、以下の関数がスキーマ全体、または現在のデータベース全体の類似マッピングを作成するため利用できます。
schema_to_xml(schema name, nulls boolean, tableforest boolean, targetns text) schema_to_xmlschema(schema name, nulls boolean, tableforest boolean, targetns text) schema_to_xml_and_xmlschema(schema name, nulls boolean, tableforest boolean, targetns text) database_to_xml(nulls boolean, tableforest boolean, targetns text) database_to_xmlschema(nulls boolean, tableforest boolean, targetns text) database_to_xml_and_xmlschema(nulls boolean, tableforest boolean, targetns text)
これらはメモリー内に作成される必要がある、多くのデータを生成する潜在的可能性があることに注意してください。 巨大なスキーマ、またはデータベースの内容マッピングを要求する際は、その代わりにテーブルを別々にマップすること、さらにはカーソル経由とすることさえ、検討することは無駄ではありません。
スキーマ内容マッピングの結果は以下のようになります。
<schemaname> table1-mapping table2-mapping ... </schemaname>
ここで、テーブルマッピング書式は上で説明したとおりtableforestパラメータに依存します。
データベース内容マッピング書式は以下のようになります。
<dbname> <schema1name> ... </schema1name> <schema2name> ... </schema2name> ... </dbname>
ここで、スキーママッピングは上記のとおりです。
これらの関数で作成された出力を使用する1つの例として、図 9.1は、テーブルデータの表形式への翻訳を含むtable_to_xml_and_xmlschemaからHTML文書への出力の変換をおこなうXSLTスタイルシートを示します。
同じようにして、これらの関数の結果は他のXML基準書式に変換されます。
図9.1 SQL/XML出力をHTMLに変換するXSLTスタイルシート
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.w3.org/1999/xhtml"
>
<xsl:output method="xml"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
doctype-public="-//W3C/DTD XHTML 1.0 Strict//EN"
indent="yes"/>
<xsl:template match="/*">
<xsl:variable name="schema" select="//xsd:schema"/>
<xsl:variable name="tabletypename"
select="$schema/xsd:element[@name=name(current())]/@type"/>
<xsl:variable name="rowtypename"
select="$schema/xsd:complexType[@name=$tabletypename]/xsd:sequence/xsd:element[@name='row']/@type"/>
<html>
<head>
<title><xsl:value-of select="name(current())"/></title>
</head>
<body>
<table>
<tr>
<xsl:for-each select="$schema/xsd:complexType[@name=$rowtypename]/xsd:sequence/xsd:element/@name">
<th><xsl:value-of select="."/></th>
</xsl:for-each>
</tr>
<xsl:for-each select="row">
<tr>
<xsl:for-each select="*">
<td><xsl:value-of select="."/></td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>