本文へジャンプ

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

送信されたすべての情報は安全です。

  • 閉じる [x]

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


送信されたすべての情報は安全です。

  • 閉じる [x]

大文字小文字を区別しない列挙

大文字と小文字の両方を使用できるようにするシンプルで、自動化されたソリューション

Doug Tidwell (dtidwell@us.ibm.com), Senior Programmer, IBM
Doug Tidwellはシニア・プログラマーであり、IBMのWeb Services伝道者です。彼は1997年に行われた最初のXML会議の講演者であり、10年以上に渡ってマークアップ言語の仕事に携わってきました。University of Georgiaの英語の学士号と、Vanderbilt Universityのコンピューター・サイエンスの修士号を有しています。連絡先はdtidwell@us.ibm.com です。 ibm.com/developerWorks/speakers/dtidwell/で彼のWebページを見ることができます。

概要: この記事で、IBMのXMLの分野のエースであるDoug Tidwell氏は、好奇心旺盛なある読者の質問に答えて、大文字小文字を区別しない列挙(enumeration)を定義するための自動化されたソリューションを紹介します。このソリューションは、理解しやすく、規格に準拠しており、デベロッパーが行う作業はわずかです。いくつかのサンプル・コードも紹介されています。

日付:  2002年 10月 01日
レベル:  初級 この記事の原文:  英語
アクティビティー: 3285 ビュー
お気軽にご意見・ご感想をお寄せください: 


developerWorksでは、読者の皆さんからの質問にできる限りお答えし、皆さんのお役に立てるよう心がけています。最近、Iowa州Des Moinesに住むTommy Jones氏から以下のような手紙をいただきました。

Regis様.
XML Schemaにおいて、大文字小文字を区別しない列挙を定義する方法はあるのでしょうか ?要素の有効値が「red」、「blue」、および「green」である場合、我々はユーザーがそれらの値を大文字小文字を組み合わせて使用できるようにしたいと考えています。XML Schema仕様では、大文字小文字を区別しない列挙を定義する方法を見つけることができませんでした。ご教示いただけるでしょうか。
よろしくお願いします。
Iowa, Des Moines, Tommy Jones

Tommyさんには、良い知らせと悪い知らせがあります。悪い知らせとは、所望の作業をXML Schemaで行うことはできないというものです。良い知らせとは、標準に準拠し、シンプルで、余分な作業が必要ない自動化ソリューションがあるというものです。

はじめに

まず最初に断っておきますと、所望の結果を直接実現する方法はありません。この問題を解決する方法は、列挙を正規表現に変換することです。使用するスキーマが以下のデータ型を定義するとしましょう。

<xsd:element name="favoriteColor">
  <xsd:simpleType>
    <xsd:restriction base="xsd:string">
      <xsd:enumeration value="red"/>
      <xsd:enumeration value="blue"/>
      <xsd:enumeration value="green"/>
    </xsd:restriction>
  </xsd:simpleType>
</xsd:element>
			

大文字小文字を区別しないで比較を行うには、正確な値すべてを組み合わせた正規表現に変換する必要があります。たとえば、値 "blue" の場合、正規表現で「大文字または小文字のBから始まって、次に大文字または小文字のL、次に大文字または小文字のU、次に大文字または小文字のEが続く」ということを表すことになります。つまり、上記のデータ型を、以下のようにします。

<xsd:element name="favoriteColor">
  <xsd:simpleType>
    <xsd:restriction base="xsd:string">
      <xsd:pattern value="((B|b)(L|l)(U|u)(E|e)) | 
                          ((G|g)(R|r)(E|e)(E|e)(N|n)) | 
                          ((R|r)(E|e)(D|d))"/>
    </xsd:restriction>
  </xsd:simpleType>
</xsd:element>
			

この正規表現は、 "blue""BlUE""bLUe" など、大文字と小文字を組み合わせたすべてのblueという語に一致します。(これは、大文字と小文字のすべての組み合わせを定義する <xsd:enumeration> 要素を使用することによっても解決できますが、特に有効値が長いストリングのような場合、正規表現が非常に長くなってしまいます。)


良い知らせ

XML Schemaはそれ自体XML文書であるため、列挙マークアップを、今見たような正規表現に変換するスタイルシートを作成することができます。これを行うには、 xsd:string データ型を制限している <xsd:restriction> 要素で、 <xsd:enumeration> 要素を含むものすべてを検索する必要があります。ここで必要なのは、検索する <xsd:restriction> 要素以外の既存のスキーマすべてをコピーするスタイルシートです。続いて、 <xsd:enumeration> 要素の変換方法を定義する規則を追加します。

XML文書をコピーするための基本ルールを定義するスタイルシートは、以下のとおりです。これは、ソース文書内のすべてに使用されるデフォルトの規則となります。この後で、これに <xsd:restriction> を変換するための規則を追加します。

<?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">

  <xsl:template match="*|@*|text()|comment()|processing-instruction()">
    <xsl:copy>
      <xsl:apply-templates select="*|@*|text()|comment()| 
                                   processing-instruction()" />
    </xsl:copy>
  </xsl:template>

  <!-- Add the stuff that handles the enumerations here. -->

</xsl:stylesheet>
			

これで、後は <xsd:restriction> 要素を変換するテンプレートを作成するだけです。以下に示すのは、要素を選択するXPath式です。

<xsl:template match="xsd:restriction[@base='xsd:string']
                     [count(xsd:enumeration) > 0]">
			

XPath構文に精通していない方のために説明しますと、この式は、スタイルシート・プロセッサーに base='xsd:string' 属性を持ち、少なくとも1つの <xsd:enumeration> 要素の入った <xsd:restriction> 要素をすべて選択するように指示します。 <xsd:restriction> 要素の中にある各 <xsd:enumeration> 要素に関して行なうことのアルゴリズムは以下のとおりです。

  1. 左括弧を書く。
  2. 各文字の大文字および小文字の値を書く。
  3. 右括弧を書く。
  4. これが最後の <xsd:enumeration> でない場合、縦線を追加する。

スタイルシートの中の該当する個所は以下のようになります。

<xsl:template match="xsd:restriction[@base='xsd:string']
                     [count(xsd:enumeration) > 0]">
  <xsd:restriction base="xsd:string">
    <xsd:pattern>
      <xsl:attribute name="value">
        <xsl:for-each select="xsd:enumeration">

          <!-- Step 1. Write a left parenthesis -->
          <xsl:text>(</xsl:text>

          <!-- Step 2. Write the upper- and lowercase letters -->

          <!-- Step 3. Write a right parenthesis -->
          <xsl:text>)</xsl:text>

          <!-- Step 4. If this isn't the last enumeration, write -->
          <!-- a vertical bar -->
          <xsl:if test="not(position()=last())">
            <xsl:text>|</xsl:text>
          </xsl:if>

        </xsl:for-each>
      </xsl:attribute>
    </xsd:pattern>
  </xsd:restriction>
<xsl:template>
			

このステップでは、各文字の大文字および小文字の値を書き出す難しいステップをスキップしていることに注意してください。この作業を行うには、tail recursion およびXSLT translate() 関数を使用します。

Tail recursionとは、XSLTスタイルシートの一般的な技法の1つです。名前付きのテンプレートを使用して、この技法を処理することができます。名前付きテンプレートは、ストリング内のすべての文字が処理されるまでそれ自体を呼び出します。テンプレート (この例では、 case-insensitive-pattern という名前が付けられている) は、2つのパラメーター(正規表現に変換するストリングと、ストリング内の開始位置) を受け取ります。名前付きのテンプレートがどのように開始されるかについては以下のとおりです。

  <xsl:template name="case-insensitive-pattern">
    <xsl:param name="string"/>
    <xsl:param name="index"/>
			

任意のストリングの有効な値は、以下を連結したものとなります。

  1. 現行文字の値。( A|a ) 形式。
  2. 残りの文字の値。( A|a ) 形式。(文字が残っていない場合、値は空になります。そうでない場合、テンプレートを再帰的に呼び出します。元のストリングを渡して、開始位置から1文字ずつ進めることによってこれを行います。)

上記の2つの値を表す2つの変数を作成して、 <xsl:value-of> 要素を使用して結合された値を出力します。現行文字について、左括弧、文字の大文字値、縦線、文字の小文字値、および右括弧を出力します。以下は、最初の変数を算出するマークアップです。

<xsl:variable name="current-letter">
  <!-- Write a left parenthesis -->
  <xsl:text>(</xsl:text>

  <!-- Convert the current letter to uppercase -->
  <xsl:value-of select="translate(substring($string, $index, 1), 
                                  'abcdefghijklmnopqrstuvwxyz', 
                                  'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>

  <!-- Write a vertical bar -->
  <xsl:text>|</xsl:text>

  <!-- Convert the current letter to lowercase -->
  <xsl:value-of select="translate(substring($string, $index, 1), 
                                  'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 
                                  'abcdefghijklmnopqrstuvwxyz')"/>

  <!-- Write a right parenthesis -->
  <xsl:text>)</xsl:text>
</xsl:variable>
			


XSLT translate() 関数についてひと言

先に進む前に、XSLT translate() 関数が3つのストリングを取ることに注目しましょう。最初のストリングの各文字について、2番目のストリングの文字 ( 'abcde...' ) は、3番目のストリングの対応する文字 ( 'ABCDE...' ) に置き換えられます。たとえば、最初のストリングが bed である場合、関数呼び出し translate('bed', 'abcde...', 'ABCDE...') BED を戻します。最初のストリング内のある文字が2番目のストリングにない場合、それは変更されません。つまり、 translate('bed7', 'abcde...', 'ABCDE...') BED7 を戻すということです。必要であれば、関数呼び出し内のストリングを拡張して、西ヨーロッパ言語で使用されるアクセント付き文字を組み込むこともできます。(XSLTの仕様によると、 translate() によって世界中の言語の大文字小文字を変換できるわけではないことが警告されていますので、その点に注意してください。)

それでは、残りの文字の値を計算して、それぞれを ( A|a ) 形式に変換しましょう。現行文字の索引がストリングの長さよりも小さい場合、名前付きテンプレートを再度呼び出して、元のストリングを渡し、1ずつ増分します。現行文字の索引がストリングの長さと同じである場合、変数は空ストリングとなります。

<xsl:variable name="remaining-letters">

  <!-- If $index is less than the length of the string, -->
  <!-- call the template again. -->
  <xsl:if test="$index < string-length($string)">
    <xsl:call-template name="case-insensitive-pattern">

      <!-- The string parameter doesn't change -->
      <xsl:with-param name="string" select="$string"/>

      <!-- Increment the index of the current letter by 1 -->
      <xsl:with-param name="index" select="$index + 1"/>
    </xsl:call-template>
  </xsl:if>
</xsl:variable>
			

最後に、 <xsl:value-of> 要素と concat() 関数を使用して、2つの変数の値を出力します。これは、他のプログラミング言語の return ステートメントと同じです。

<xsl:value-of select="concat($current-letter, $remaining-letters)"/>
			

bluered 、および green が正しい値の場合、スタイルシートを使用してスキーマを変換し、新しいスキーマを生成できます。新しいスキーマを使用すると、 BLUEBluebLuE 、および blUE はすべて妥当となります。


ではこれから、作成したスタイルシートがどのように機能するのかを示す例をあげましょう。ここでは、性別、既婚/独身、および好きな色に関する表記を定義するスキーマを使用します。サンプルのインスタンス文書は以下のものです。

<?xml version="1.0"?>
<f:friend 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ibm.com/developerWorks 
                    friend.xsd"
  xmlns:f="http://www.ibm.com/developerWorks">
  <f:name>
    <f:firstName>Jane</f:firstName>
    <f:lastName>Doe</f:lastName>
  </f:name>
  <f:gender>f</f:gender>
  <f:maritalStatus>married</f:maritalStatus>
  <f:favoriteColor>orange</f:favoriteColor>
</f:friend>
			

この例では、小さなJavaコードである XMLValidator.java を組み込んで、XMLスキーマに対してXML文書をバリデートします。 java XMLValidator friend.xml と入力すると、以下のように表示されます。

> java XMLValidator friend.xml

Your document contains no errors!
			

このサンプル文書では、値 fmarried 、および orange はすべて大文字小文字を区別します。そのため、 FMarried 、または OrAnGE と入力するとエラーが発生します。これらの無効な値を friend.xml に入れると、以下のようなメッセージが表示されます。

Error in friend.xml at line 10, column 25: cvc-type.3.1.3: The value 'F' of 
element 'f:gender' is not valid.
Error in friend.xml at line 11, column 45: cvc-type.3.1.3: The value 'Married' 
of element 'f:maritalStatus' is not valid.
Error in friend.xml at line 12, column 44: cvc-type.3.1.3: The value 'OrAnGE' 
of element 'f:favoriteColor' is not valid.
			

ここで、作成したXSLTスタイルシートを使用して、オリジナルのスキーマを新しいスキーマ文書に変換します。

> java org.apache.xalan.xslt.Process -in friend.xsd -xsl convert-enumerations.xsl 
-out insensitive-friend.xsd
			

次に、XML文書のルート要素を変更して、以下に示す新しいスキーマ・ファイルを参照するようにします。

<f:friend 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.ibm.com/developerWorks 
                      insensitive-friend.xsd"
  xmlns:f="http://www.ibm.com/developerWorks">
			

ここでXML文書にバリデーション・プログラムを実行すると、今回は文書にエラーがないことを示すメッセージが表示されます。ファイル case-insensitive.zip には、これらを行うのに必要なすべてのコードとサンプルが入っています。

Tommy! この記事があなたの疑問に対する答えになることを願います。今回紹介したソリューションは、比較的シンプルであり、自動的に機能するものであり、さらにXMLの規格に基づいたものです。

あなたも疑問をお持ちですか ?そうでしたら、どうぞ我々までメールをお送りください。あり余っている時間を使ってお答えしたいと思います。


参考文献

著者について

Doug Tidwellはシニア・プログラマーであり、IBMのWeb Services伝道者です。彼は1997年に行われた最初のXML会議の講演者であり、10年以上に渡ってマークアップ言語の仕事に携わってきました。University of Georgiaの英語の学士号と、Vanderbilt Universityのコンピューター・サイエンスの修士号を有しています。連絡先はdtidwell@us.ibm.com です。 ibm.com/developerWorks/speakers/dtidwell/で彼のWebページを見ることができます。

不正使用の報告のヘルプ

不正使用の報告

ありがとうございます。 このエントリーは、モデレーターの注目フラグが設定されました。


不正使用の報告のヘルプ

不正使用の報告

不正使用の報告の送信に失敗しました。


developerWorks: サイン・イン


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。 プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。 お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

表示名をお選びください

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

(半角英数字で3文字以上31文字以下にする必要があります)


「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


この記事を評価する

コメント

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=240094
ArticleTitle=大文字小文字を区別しない列挙
publish-date=10012002
author1-email=dtidwell@us.ibm.com
author1-email-cc=

タグ

Help
このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。

スライダーバーを使用することで、より多く(少なく)タグを表示します。

人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。

マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。

このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。