そごうソフトウェア研究所

SOA、開発プロセス、ITアーキテクチャなどについて書いています。Twitterやってます@rsogo

XQueryによる要素抽出と文字列操作

XQueryちょっと好きになってきました。XQueryによる要素抽出と文字列操作を、ちょっとだけ複雑な題材をつかって実際にやってみます。

題材として使用するのは、次のようなスキーマXMLです。
SimpleRecords要素が繰り返します。

<xsd:element name="SimpleServiceMsg">
    <xsd:complexType>
      <xsd:sequence>
          <xsd:element name="SimpleRecords" minOccurs="1" maxOccurs="unbounded">
             <xsd:complexType>
                  <xsd:sequence>
                      <xsd:element name="input0" type="xsd:string"/>
                      <xsd:element name="input1" type="xsd:string"/>
                      <xsd:element name="input2" type="xsd:string"/>
                      <xsd:element name="input3" type="xsd:string"/>
                      <xsd:element name="input4" type="xsd:string"/>
                      <xsd:element name="input5" type="xsd:string"/>
                      <xsd:element name="input6" type="xsd:dateTime"/>
                      <xsd:element name="input7" type="xsd:boolean"/>
                      <xsd:element name="input8" type="xsd:decimal"/>
                      <xsd:element name="input9" type="xsd:integer"/>
                  </xsd:sequence>
             </xsd:complexType>
          </xsd:element>
       </xsd:sequence>
   </xsd:complexType>
</xsd:element> 

変換要件1

  • XQueryで、input0要素の値の文字長が9桁であるSimpleRecords要素を抽出。
  • このときinput0要素の値の先頭を0埋めして10桁にする。

文字長が9桁でないものは、スキップする。

XQuery回答例

文字長が9桁のものだけを抽出するには、forとwhereを組み合わせます。文字長を求めるのはfn:string-length関数を使います。

                for $SimpleRecords in $simpleServiceMsg1/SimpleRecords
                                where fn:string-length($SimpleRecords/ns2:input0) = 9


文字列連結はfn:concat関数を使います。XQueryの関数を調べるにはW3CのRecommendationが便利です。
http://www.w3.org/TR/xpath-functions/

                            let $result := fn:concat("0", $SimpleRecords/ns2:input0/text())
                            return
                                $result[1]


全体としては、次のような感じ。全体のメッセージの中のinput0要素の値だけ、文字列操作が必要になるので、SimpleRecords要素を作るところと、input0要素を作るところが入れ子になって、メッセージを作っています。

        <SimpleServiceMsg>
            {
                for $SimpleRecords in $simpleServiceMsg1/SimpleRecords
                                where fn:string-length($SimpleRecords/ns2:input0) = 9
                return
                    <SimpleRecords>
                    <input0>
                        {
                            let $result := fn:concat("0", $SimpleRecords/ns2:input0/text())
                            return
                                $result[1]
                        }</input0>
                        <input1>{ data($SimpleRecords/input1) }</input1>
                        <input2>{ data($SimpleRecords/input2) }</input2>
                        <input3>{ data($SimpleRecords/input3) }</input3>
                        <input4>{ data($SimpleRecords/input4) }</input4>
                        <input5>{ data($SimpleRecords/input5) }</input5>
                        <input6>{ data($SimpleRecords/input6) }</input6>
                        <input7>{ data($SimpleRecords/input7) }</input7>
                        <input8>{ data($SimpleRecords/input8) }</input8>
                        <input9>{ data($SimpleRecords/input9) }</input9>
                    </SimpleRecords>
            }
        </SimpleServiceMsg>

こういうのが10分くらいですらすら書けるようになるといいな。