如何从紧随其后的兄弟中获取属性并将其与当前元素合并为一个元素

时间:2013-11-14 12:23:51

标签: xml xslt

我是XSLT的新手,我正在尝试转换以下XML:

<array>
   <!-- red herring -->
   <Telop ID="t01">
      <time>0</time>
      <Sentence />
   </Telop>
   <!-- start time and label -->
   <Telop ID="t02">
      <time>14</time>
      <Sentence>Subtitle 1</Sentence>
   </Telop>
   <!-- end time -->
   <Telop ID="t03">
      <time>26</time>
      <Sentence />
   </Telop>
   <!-- start time and label -->
   <Telop ID="t04">
      <time>44</time>
      <Sentence>Subtitle 2</Sentence>
   </Telop>
   <!-- end time -->
   <Telop ID="t05">
      <time>48</time>
      <Sentence />
   </Telop>
</array>

进入以下结构,其中备用节点提供开始时间和标签,紧接着的兄弟提供结束时间:

    <div>
        <p begin="00:00:14.000" end="00:00:26.000">Subtitle 1</p>
        <p begin="00:00:44.000" end="00:00:48.000">Subtitle 2</p>
    </div>

我拼凑了以下内容:

<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
    <div xml:lang="en" style="1">
        <xsl:for-each select="array/Telop">
            <xsl:if test="not(position() mod 2)">
                <p>
                    <xsl:attribute name="begin">
                        <xsl:value-of select="time" />
                    </xsl:attribute>
                    <xsl:attribute name="end">
                        <!-- How can I get the time from the immediate following sibling? -->
                        <xsl:value-of select="/following-sibling::time" />
                    </xsl:attribute>
                    <xsl:value-of select="Sentence" />
                </p>
            </xsl:if>
        </xsl:for-each>
    </div>
</xsl:template>

这非常接近,但我无法弄清楚如何为后续兄弟提取时间值。

3 个答案:

答案 0 :(得分:3)

这个XSLT应该可以胜任。

我冒昧或删除了“xslt:for-each”(被认为是ucky)和“xslt:if”(可以合并)

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/array">
  <div xml:lang="en" style="1">
      <xsl:apply-templates select="Telop[not(position() mod 2)]" />    
    </div>
</xsl:template>

<xsl:template match="Telop">
  <p>
    <xsl:attribute name="begin">
      <xsl:value-of select="time" />
    </xsl:attribute>
    <xsl:attribute name="end">
      <xsl:value-of select="following-sibling::Telop[1]/time" />
    </xsl:attribute>
    <xsl:value-of select="Sentence" />
</p>

</xsl:template>
</xsl:transform>

希望这有帮助,

答案 1 :(得分:1)

这是一种略有不同的方法,因为它不依赖于position()的模2。相反,这一行

<xsl:for-each select="Telop[Sentence/text()]">

检查Sentence是否包含文字。至于xsl:for-each的丑陋,这是事实,但它确实起到了作用。

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" indent="yes"/>

<xsl:template match="/">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="array">
  <xsl:element name="div">
     <xsl:for-each select="Telop[Sentence/text()]">
        <xsl:element name="p">
              <xsl:attribute name="begin">
                 <xsl:value-of select="./time"/>
              </xsl:attribute>
              <xsl:attribute name="end">
                 <xsl:value-of select="following-sibling::Telop[1]/time"/>
              </xsl:attribute>
              <xsl:value-of select="Sentence"/>
        </xsl:element>
     </xsl:for-each>
  </xsl:element>   
</xsl:template>

</xsl:stylesheet>

答案 2 :(得分:0)

我确定的解决方案(感谢Marvin和Mathias),并将秒值的格式设置为小时:分钟:秒(在此answer的帮助下)如下:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/array">
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:tts="http://www.w3.org/ns/ttml#styling" xml:lang="en">
    <head>
      <styling>
            <style tts:textOutline="black 1px 0px" tts:color="white" xml:id="1" tts:textAlign="center" tts:padding="0px" tts:fontSize="16px" tts:fontFamily="Arial, Helvetica, sans-serif"/>     
        </styling>
    </head>
    <body>    
        <div xml:lang="en" style="1">
            <xsl:apply-templates select="Telop[not(position() mod 2)]" />    
        </div>
    </body>
</tt>
</xsl:template>

<xsl:template match="Telop">
    <xsl:element name="p">
        <xsl:attribute name="begin">
            <xsl:call-template name="format-time">
                <xsl:with-param name="value" select="time" />
            </xsl:call-template>
        </xsl:attribute>
        <xsl:attribute name="end">
            <xsl:call-template name="format-time">
                <xsl:with-param name="value" select="following-sibling::Telop[1]/time" />
            </xsl:call-template>
        </xsl:attribute>
        <xsl:value-of select="Sentence" />
    </xsl:element>
</xsl:template>

<xsl:template name="format-time">

    <xsl:param name="value" select="." />
    <xsl:param name="alwaysIncludeHours" select="true()" />
    <xsl:param name="includeSeconds" select="true()" />

    <xsl:if test="$value > 3600 or $alwaysIncludeHours">
        <xsl:value-of select="concat(format-number($value div 3600, '00'), ':')"/>
    </xsl:if>

    <xsl:value-of select="format-number(floor($value div 60), '00')" />

    <xsl:if test="$includeSeconds">
        <xsl:value-of select="concat(':', format-number($value mod 60, '00'))" />
    </xsl:if>
</xsl:template>

</xsl:transform>