0

次のような XML があります。

<TEI>
  <text>
    <div type="scene" n="1">
      <sp xml:id="sp1">
        <speaker>Julius</speaker>
        <l>Lorem ipsum dolor sit amet</l>
        <ptr cRef="..." />
        <stage>Aside</stage>
        <ptr cRef="..." />
        <l>consectetur adipisicing elit</l>
        <stage>To Antony</stage>
        <l>sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</l>
      </sp>
      <sp xml:id="sp2">
        ...

そして、すべての<stage>要素を 1 レベル上に持ち上げて s の兄弟になる必要があります。たとえば、要素が 内の他の要素との前後関係を保持するように s<sp>を分割します。<sp><stage><sp>

<TEI>
  <text>
    <div type="scene" n="1"> 
     <sp by="#Julius">
       <l>Lorem ipsum dolor sit amet</l>
       <ptr cRef="..." />
     </sp>
     <stage>Aside</stage>
     <sp by="#Julius">
       <ptr cRef="..." />
       <l>consectetur adipisicing elit</l>
     </sp>
     <stage>To Antony</stage>
     <sp by="#Julius">
       <l>sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</l>
     </sp>

これを行うために XSLT に取り組んできました。<sp>これには、最初の子まで (ただし、最初の<stage>子は含まない) のすべての子要素を消費し、 new の子として結果ツリーに出力することを目的とした再帰的なテンプレートが含まれてい<sp>ます。<stage>次に、最初の要素を放出します。そして、その最初の要素に続くすべての要素を再帰します<stage>。最終的に、子要素のリストに<stage>s が残っていない場合、残りのすべての要素が結果ツリーの new 内に出力され<sp>ます。デバッグを含むコードは<xsl:message>次のとおりです。

<xsl:template name="sp-with-stage">
  <!-- call with speaker -->
  <xsl:param name="speaker" />
  <!-- call with an <sp> element -->
  <xsl:param name="sp" />
  <!-- $content parameter is optional, by default it's the children of the given $sp; this is the parameter whose value is different with each recursive call -->
  <xsl:param name="content" select="$sp/*" />
  <!-- find the first <stage> element amongst the $content node set -->
  <xsl:variable name="stage" select="$content/following-sibling::stage[1]" />

  <xsl:message>ID = <xsl:value-of select="$sp/@xml:id" /></xsl:message>
  <xsl:message>speaker = "<xsl:value-of select="$speaker" />"</xsl:message>
  <xsl:message>content length = <xsl:value-of select="count($content)" /></xsl:message>
  <xsl:if test="$stage">
  <xsl:message>nodes before $stage = <xsl:value-of select="count($stage/preceding-sibling::*)" /></xsl:message>
  <xsl:message>nodes after $stage = <xsl:value-of select="count($stage/following-sibling::*)" /></xsl:message>
  </xsl:if>

  <xsl:if test="$stage">
    <sp by="#{$speaker}">
      <!-- process all the nodes in the $content node set before the current <stage> -->
      <xsl:message>Processing <xsl:value-of select="count($stage/preceding-sibling::*)" /> nodes before "<xsl:value-of select="$stage/text()" />"</xsl:message>
      <xsl:apply-templates select="$stage/preceding-sibling::*" />
    </sp>
    <xsl:apply-templates select="$stage" />
  </xsl:if>
  <xsl:choose>
    <xsl:when test="$stage/following-sibling::stage">
      <!-- if there's another <stage> element in the $content node set then call this template recursively -->
      <xsl:message>Call recursively with <xsl:value-of select="count($stage/following-sibling::*)" /> following nodes</xsl:message>
      <xsl:call-template name="sp-with-stage">
        <xsl:with-param name="speaker"><xsl:value-of select="$speaker" /></xsl:with-param>
        <xsl:with-param name="sp" select="$sp" />
        <!-- the $content node set for this call is all the nodes after the current <stage> -->
        <xsl:with-param name="content" select="$stage/following-sibling::*" />
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="$stage/following-sibling::*">
      <!-- if there's no <stage> element in the $content node set, but there are still some elements, emit them in an <sp> element -->
      <sp by="#{$speaker}">
        <xsl:message>Processing <xsl:value-of select="count($stage/following-sibling::*)" /> trailing nodes</xsl:message>
        <xsl:apply-templates select="$stage/following-sibling::*" />
      </sp>
    </xsl:when>
  </xsl:choose>
</xsl:template>

このテンプレートは、次のように呼び出されます。

<xsl:template match="sp[stage]">
  <xsl:call-template name="sp-with-stage">
    <xsl:param name="speaker"><xsl:value-of select="speaker" /></xsl:param>
    <xsl:param name="sp" select="." />
  </xsl:call-template>
</xsl:template>

問題は、現在のノードの前にある現在のノード セット$stage/preceding-sibling::*のノードだけを処理するという私の使用法にあります。実際に起こることは、すべての再帰呼び出しで、元のコンテキストから現在のノードに先行するすべてのノードが this によって選択されるということです。これは、再帰呼び出しが毎回正しい新しいノード セットを取得し、ノードがその正しいノード セットから取得されているにもかかわらずです。$content$stage$stage<sp>$stage/preceding-sibling::*$content$stage$content

明確にするために、上記の XML の例の場合、<stage>To Antony</stage>$stageノードであり、$contentノードに次のものが含まれている場合:

<l>consectetur adipisicing elit</l>
<stage>To Antony</stage>
<l>sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</l>

この式は、までのオリジナルのすべて$stage/preceding-sibling::*の子を引き続き生成します。<sp><stage>To Antony</stage>

preceding-sibling私が正しく理解していないことについて何かがあるに違いないと思います。助言がありますか?または、変換を達成するためのまったく異なる方法の提案さえありますか?

4

2 に答える 2

0

これはグループ化の問題です。それぞれの内部のすべての要素(とspを除く) を、最も近い先行(存在する場合) でグループ化する必要があります。これに対する XSLT 1.0 での標準的なアプローチは、Muenchian グループ化と呼ばれます。グループ化基準を与えるキーを定義し、トリックを使用して各グループの最初のノードをグループ全体のプロキシとして処理します。speakerstagestagegenerate-id

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes" />

  <!-- group first by the parent sp and then by the nearest preceding stage.
       generate-id(emptynodeset) is the empty string by definition, so this
       is still well defined for the elements before the first stage in an sp -->
  <xsl:key name="groupKey" match="sp/*[not(self::speaker | self::stage)]" use="
     concat(generate-id(..), '|', generate-id(preceding-sibling::stage[1]))" />

  <!-- identity template - copy everything as-is unless overridden -->
  <xsl:template match="@*|node()">
    <xsl:copy><xsl:apply-templates select="@*|node()" /></xsl:copy>
  </xsl:template>

  <xsl:template match="sp">
    <!-- for each group -->
    <xsl:for-each select="*[generate-id() = generate-id(key('groupKey',
          concat(generate-id(..), '|', generate-id(preceding-sibling::stage[1]))
        )[1])]">
      <!-- the "stage" if there is one - if we are before the first stage in this
           sp then the preceding-sibling:: will select nothing -->
      <xsl:apply-templates select="preceding-sibling::stage[1]" />
      <sp by="#{../speaker}">
        <!-- the following elements up to the next stage -->
        <xsl:apply-templates select="key('groupKey',
          concat(generate-id(..), '|', generate-id(preceding-sibling::stage[1]))
        )" />
      </sp>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

stageこれは入力例で機能しますが、2 つの連続した要素がその間に何もないインスタンスがある場合は、いくつかの変更が必要になる場合があります。

于 2014-03-21T12:15:24.340 に答える