XMLの変換

はじめに

Wolfram言語はパターンマッチング機能がパワフルで,組込みの構造的な操作関数も数多く含んでいるので,記号式の処理には比類なく秀でている.このチュートリアルではXMLデータの処理にWolfram言語を使う例をいくつかご紹介する.

任意のXMLドキュメントをWolfram言語にインポートするとき,このドキュメントは自動的に記号的なXML式に変換される.記号的なXMLはXMLドキュメントをWolfram言語のシンタックスで表すために使われる形式である.XMLドキュメントは記号的なXMLに変換される際,その構造を保ったままWolfram言語式に変換される.XMLデータを記号的なXMLに変換することによって,Wolfram言語の組込み関数を使って直接記号的なXMLが操作できるようになる.

XMLを記号的なXMLに変換する.
リスト中の不必要な"red"要素を削除するために,簡単な変換規則を使う.
ExportStringを使うと,記号的なXMLを読みやすいように設計されたネイティブXMLシンタックスに変換される.

XML木構造の可視化

多くのXMLツールは,XMLドキュメントを折りたためる木構造で表示する.この木構造では,ノードはドキュメントの要素に相当する.ここではWolfram言語ノートブック中でセルのグループ化を使い,どのようにして同様の視覚化を行うかを示す.

このために記号的なXML式を再帰的に見て行き,各XMLElementオブジェクトに対して,そのXMLElementオブジェクトの各属性と子に対するセルを含むCellGroupData式を作成する.ネストした各CellGroupData式は,その前のものに対してインデントされる.ここではまずXMLElementオブジェクトを処理する関数から始める.

ここではインデントに整数 m を使っていることに注意されたい.XMLNoteXMLElementオブジェクトの子にマップするとき,子要素のインデントを増やすために,より大きな値の m を渡す.

CellGroupData式はセルのリストを含んでいる.定義では,実際にセルを作ったのはXMLElementx に対してのみである.しかし,それからXMLNoteを属性のリストにマップした.これはリストを返すので,結果に対してApply[Sequence]を使って,リストをCellGroupData式のセルのリストに結合しなければならない.それからXMLElementの子についても同様のことを行う.

しかし,属性に対して使えるXMLNoteをまだ定義していない.XMLElementオブジェクトの属性は記号的なXMLに規則として保存される.多くの場合,この規則はキーと値の2つの文字列を含んでいる.しかし,名前空間が関係している場合,規則の最初の要素は名前空間とキーの2つの文字列を含むリストであるかもしれない.そこで属性を扱うために2つの定義が必要になる.

簡単な記号的なXML式を処理するためには定義がもう1つ必要である.XMLドキュメントのテキストノードは記号的なXMLでは単純にStringオブジェクトとして保管される.従って,Stringオブジェクトを扱う定義が必要なのである.

基本的なXMLドキュメントを可視化する簡単なノートブックを構築する.

"IncludeEmbeddedObjects"オプションのデフォルトの値はNoneであるので,コメント,処理命令,あるいはXMLObjectに保管される他のどのようなものにも変更を加えなかった.これらのものに対する定義を追加することは難しいことではなく,記号的なXMLを処理する上でよい練習となるであろう.

XMLデータの操作

XMLアプリケーションはドキュメントレイアウト以外のことにも使われる.XMLは構造化されたデータを保管するのにも優れた形式である.多くの商用データベースの製造元は,製品にXMLのサポートを組み込むようになった.これにより,中間形式としてXMLを使ってデータベースを操作できるようになった.

Wolfram言語には記号的パターンマッチング機能があるため,XMLドキュメントから情報を抽出したり操作したりするのに理想的である.例として,XMLファイルに収められた野球のメジャーリーグ選手のデータを使ってみる.

このファイルを記号的なXMLとしてWolfram言語にインポートする.
各プレーヤーの情報はPlayerRecord要素に入っている.Casesを使ってこれを取り出す.
このXMLドキュメントには294人のプレーヤーに関する記録が含まれている.各PlayerRecord要素の中には,プレーヤーが所属しているチームを指定するTEAM要素がある.少し高度なパターンをCasesに渡すことで,Yankeesチームに所属している全プレーヤーを抽出することができる.
これで変数yankeesにすべてのYankeesプレーヤーに対する記号的なXML式のリストが割り当てられる.yankeesの最初の要素を見てみる.
プレーヤーの名前は,各PlayerRecord要素のPLAYER要素に保管されている.PlayerRecord 要素の1つから,名前を抽出する.
Mapを使ってyankeesからすべての名前を抽出する.
代りに,yankeesに対して適切なパターンでCasesを使う.

記号的なXMLは任意のXMLデータを表すための汎用形式である.時には記号的なXMLを異なるタイプのWolfram言語式に変換した方が使いやすいこともあるだろう.このような変換は,パターンマッチングを使って行うと簡単である.

野球の投手についてのデータを含んでいるXMLファイルをインポートし,記号的なXMLを規則のリストに変換する.

プレーヤーに関するすべての情報は,Pitcherという頭部を持つWolfram言語規則のリストに保管されている.

データを別の式のシンタックスに変換することに加え,データを変更し,式全体を記号的なXMLのまま残しておくこともできる.このようにするとデータが変更できるが,やはり他のアプリケーションで使えるようにデータをXMLファイルにエキスポートする.

年俸が与えられていないPlayerRecord項目を削除する.
名前と年俸のペアを抽出する関数を作る.それからこのペアを年俸でソートし,上位10人を見てみる.
記号的なXML式のデータを変更する例として,プレーヤーの年俸を2倍にする関数を作る.

XSLTとWolfram言語の比較

あるXML形式のドキュメントを他の形式のものに変換する必要があることが多々ある.この目的で使われる一般的な技術のひとつにXSLT変換がある.Wolfram言語にはパワフルなパターンマッチング・変換機能があり,もとのドキュメントをインポートし,記号的なXML式を操作すると,同様の変換が簡単に行える.このセクションでは基本的なXSLT変換と,それと同様のWolfram言語を使った変換の例を見ていく.

簡単なテンプレート

この例では,XML方言がcodeタグを使ってプログラムコードを囲む.通常これは等幅フォントで表示される.このようなドキュメントをXHTMLに変換する場合,恐らくコードに対してpreタグを使うのが一般的であろう.

<xsl:template match="code">     <pre class="code">         <xsl:value-of select="."/>     </pre> </xsl:template>

Wolfram言語で同じことを行う関数が作れる.

属性の値を挿入する

新しい項目の定義を示すtermdef を使用するXMLアプリケーションを考えてみる.ここで,ドキュメントの目的の場所に直接リンクされるように,定義にa要素でアンカータグを付加する.termdef 要素内にどのような文字列形式が存在しても扱うことのできるテンプレートがあると仮定すると,以下のXSLTを使うことができる.

<xsl:template match="termdef">     <span class="termdef">         <a name="{@id}">[Definition:]&nbsp;&nbsp;</a>         <xsl:apply-templates/>     </span> </xsl:template>

結果として得られるXHTML中のname属性がもとのtermdef要素のid属性の値を持っていることに注意されたい.

Wolfram言語ではこれを以下のようにして行うことができる.

記述を使用する

より複雑な例として,XPath記述を使うものを考えてみる.note要素がexampleに設定されているrole属性を持っている場合,またはeg子要素を含んでいる場合のみnote属性をマッチさせたいとする.以下のXSLTテンプレートが何を行っているのかを見てみる.

<xsl:template match="note[@role='example' or child::eg]">     <div class="exampleOuter">         <div class="exampleHeader">Example</div>         <xsl:if test="*[1][self::p]">             <div class="exampleWrapper">                 <xsl:apply-templates select="*[1]"/>             </div>         </xsl:if>         <div class="exampleInner">             <xsl:apply-templates select="eg"/>         </div>         <xsl:if test="*[position()>1 and self::p]">             <div class="exampleWrapper">                 <xsl:apply-templates                     select="*[position>1 and self::p]"/>             </div>         </xsl:if>     </div> </xsl:template>

初めのxsl:if要素は最初の子要素がp要素であるかどうかを確認する.p要素である場合はその子要素に対してxsl:apply-templatesが呼び出される.これはCasesの結果に対してMapを呼び出すのに似ている.2つ目のxsl:if要素では1つ目の子要素の先にp要素が存在するかどうかを確認する.存在する場合は,それらについてxsl:apply-templatesが呼び出される.これに対応するWolfram言語のコードを以下に示す.

上の階層を見る

先祖や兄弟の要素を選ぶには,XMLドキュメントは単に文字のストリームであり,それが文法規則に従っているだけであるということを理解しなければならない.XMLドキュメントを操作するツールは何らかのモデルに従ってXMLを扱う.XSLT(およびそのパス選択言語,XPath)の場合は,このモデルは木構造である.Wolfram言語はリストベースの言語なので,XMLをネストした式のリストとして扱う.

この2つのモデルは似ているが,重要な違いがある.最大の違いは,ネストしたリストでは,含んでいるリストのコンセプトは本質的には持たないということである.理論的には,先祖等の軸タイプでできる変換はどれもそれがなくても行える.しかし,XMLドキュメントの上方を見ることもしばしば有用である.

次の例で,Wolfram言語で同じ動作を行うものをどのように実装するかについて考えてみる.以下のXMLドキュメントを考える.

bibref要素にマッチするテンプレートが必要であり,それを対応するbibl要素内のテキストと置き換えるとする.XSLTでは以下のようなテンプレートになる.

<xsl:template match="bibref">     <xsl:param name="ref">         <xsl:value-of select="@ref"/>     </xsl:param>     <xsl:value-of select="/bibliography/bibl[@id = $ref][1]"/> </xsl:template>

Wolfram言語で同じアプローチを取った場合,一旦bibref要素にマッチすると,それを含んでいる要素についての情報は何もなくなってしまうという問題が生じる.これを回避するために,代りに記号的なXML式全体を含んでいる式を渡す.問題のbibref要素は以下から得られる.
この式はHoldでラップして渡すことができる.ReleaseHoldを呼び出すことによりbibref要素が簡単に得られ,Part式からインデックスを削除することにより先祖にアクセスできる.しかし,これらの関数の定義にマッチできるようにパターンマッチング関数を書く必要がでてくる.
Wolfram言語での変換は比較的簡単になる.

ノートブックのHTMLへの変換

ファイル 別名で保存ダイアログのファイルの種類サブメニューにリストされている形式ではなく,ある特定のXML形式にノートブックをエキスポートする必要があるとする.ひとつの方法として,NotebookMLにエキスポートした後,外部のツール(XSLT規則等)を使ってXMLの希望の形式に変換するということが挙げられる.しかし,Wolfram言語内で操作を行い,ノートブック式を直接記号的なXMLに変換し,それを保存するという方法も簡単であることがある.Wolfram言語パターンとプログラミングの基礎的なコマンドをご存知の方ならこれができるであろう.Wolfram言語式は基本的には木構造で,その手法はXSLTによく似ているので,XSLTの知識を持つユーザは前に同じようなものを見たことがあると感じるかもしれない.

例としてファイル 別名で保存 Web Page (*.html)機能の簡単なものを作り直してみる.

例題用ノートブックを作る.

ここでは,もとのノートブック式を最初から最後まで処理するために,再帰的な関数transformを定義する.これはXSLTのテンプレートと似ている.

他のパターンに明示的にはマッチしないものをすべて削除するために,デフォルトの定義を作る.

transformは再帰的に適用されるため,定義では Sequence[]が使ってある.「null」の結果に最適なのは,シンタックスに従ったまま引数のリストの中央に入ることができるものである.

ノートブック式から始める.

注意:

記号的なXMLのシンタックスの検証

XML`SymbolicXMLErrorsを使うと,記号的なXML式のエラーを見付けることができる.この関数は,PartExtract等の関数を使って記号的なXML式の問題の部分にアクセスするときに使える場所指定を返す.

アメリカンリーグの打者のデータを含むXMLファイルをインポートする.
Salaryが"#N/A"であるプレーヤーは年俸が$1,000,000であるとする.変換を行うが,見落としやすい誤りをおかしてしまい,3つ目の要素に文字列"1000000"を使ってしった.ここはこの文字列を含むリストでなければならない.
Exportは変更されたXMLをファイルに書き出すときに,エラーを生成するす.SymbolicXMLErrorsを使って問題の式を探す.
最初にメッセージXMLElement::nameが表示されて,何らかの誤りがあることを示す.しかしSymbolicXMLErrorsの出力は間違っている箇所を厳密に示す.ALerrorsはエラーが生じた箇所を示す部分指定のリストを含んでいる.最初のエラーを検証する.
この問題は簡単に直せる.
残りのエラーも同じ性質であることが分かる.
Mapを使って,同じように残りのエラーを正す.
さらにもう1度SymbolicXMLErrorsを使い,エラーがなくなったことを確認する.