【问题标题】:Multiple Attributes to Nested Elements嵌套元素的多个属性
【发布时间】:2014-03-13 12:27:53
【问题描述】:

我想将下面的示例 sn-p 转换为多个元素。

<para font="Times" size="12" face="Bold Italic">This is some text.</para>
<para font="Times" size="12" face="Bold">This is some more text.</para>

我想把它转换成这样的东西:

<para>
   <font name="Times" size="12">
       <b>
           <i>This is some text.</i>
       </b>
    </font>
</para>
<para>
   <font name="Times" size="12">
       <b>This is some text.</b>
    </font>
</para>

有没有办法在不使用所有 &lt;xsl:choose&gt; 块的母亲的情况下做到这一点..?以上是具有更多属性和属性值的事物的简单示例。

我正在使用 XSLT 1.0 (xsltproc)

【问题讨论】:

  • 为什么第二个paraface="Bold" 并导致&lt;i&gt;This is some text.&lt;/i&gt;?另外,规则不明确。 &lt;b&gt; 应该始终是外部元素吗?最后,对此的解决方案可能对您没有用,因为这是一个“简单示例”,而您实际上拥有“更多属性和值”。如果您简化 XML,请确保保留与问题相关的复杂性
  • 老鼠,你是对的。我有一个错字已修正。
  • 我认为这是一个有代表性的例子。 的顺序并不重要。
  • 这个问题不是“定义明确的”。一方面,如果没有某种查找数组,就无法将“Bold”转换为&lt;b&gt;,将“Italic”转换为&lt;i&gt;。我们需要知道查找过程中包含什么(或至少可以包含什么)。如果只是@face 属性中列出的样式,那么这个可能 相对简单。如果是“更多的属性和属性值”,那么可能不是。
  • 也许不是最“明确”的问题,但所有答案都很好,帮助我以不同的方式看待问题。我最终只使用了带有查找文件的递归模板。

标签: xml xslt xslt-1.0


【解决方案1】:

我认为您可能仍然会在此处使用 xsl:choose,将“Bold”和“Italic”之类的词映射到“b”和“I”的元素名称

因此,您将从一个与 @face 属性匹配的模板开始,但该模板也有一个名称,因此可以递归调用它的任何“面”值仍有待处理

<xsl:template match="@face" name="face">
    <xsl:param name="face" select="." />

您可以通过一些简单的字符串处理提取第一个要处理的人脸名称

<xsl:variable name="facename" select="substring-before(concat($face, ' '), ' ')" />

(这里的concat是为了允许参数中只有一个单词)

然后你可以像这样映射到一个元素名称

    <xsl:variable name="element">
        <xsl:choose>
            <xsl:when test="$facename = 'Bold'">b</xsl:when>
            <xsl:when test="$facename = 'Italic'">i</xsl:when>
        </xsl:choose>
    </xsl:variable>

话虽如此,理论上您可以将映射保存在一个单独的 XML 文件中,并使用 document 函数来查找它们。

最后,如果找到一个元素,您将有一个 xsl:choose 来输出该元素,或者处理父元素的子元素

    <xsl:choose>
        <xsl:when test="$element != ''">
            <xsl:element name="{$element}">
                <xsl:call-template name="face">
                    <xsl:with-param name="face" select="normalize-space(substring-after($face, $facename))" />
                </xsl:call-template>
            </xsl:element>
        </xsl:when>
        <xsl:otherwise>
            <xsl:apply-templates select="../node()" />
        </xsl:otherwise>
    </xsl:choose>

试试这个 XSLT

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

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[@font]">
        <xsl:copy>
            <font>
                <xsl:apply-templates select="@*[name() != 'face']"/>
                <xsl:apply-templates select="@face" />
            </font>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="@face" name="face">
        <xsl:param name="face" select="." />

        <xsl:variable name="facename" select="substring-before(concat($face, ' '), ' ')" />

        <xsl:variable name="element">
            <xsl:choose>
                <xsl:when test="$facename = 'Bold'">b</xsl:when>
                <xsl:when test="$facename = 'Italic'">i</xsl:when>
            </xsl:choose>
        </xsl:variable>

        <xsl:choose>
            <xsl:when test="$element != ''">
                <xsl:element name="{$element}">
                    <xsl:call-template name="face">
                        <xsl:with-param name="face" select="normalize-space(substring-after($face, $facename))" />
                    </xsl:call-template>
                </xsl:element>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="../node()" />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

【讨论】:

    【解决方案2】:

    假设@face 的值实际上对应于您输出中的元素名称(很多人已经对此发表了评论),您可以这样做。

    递归命名模板实际上是对@face属性的内容进行标记,以确定嵌套顺序。

    样式表

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:exsl="http://exslt.org/common"
        extension-element-prefixes="exsl">
    
        <xsl:strip-space elements="*"/>
        <xsl:output omit-xml-declaration="yes" indent="yes" />
    
        <xsl:template match="/root">
            <xsl:copy>
                <xsl:apply-templates/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="para">
            <xsl:copy>
                <font name="{@font}" size="{@size}">
                            <xsl:call-template name="tokenize">
                                <xsl:with-param name="string" select="@face"/>
                                <xsl:with-param name="delim" select="' '"/>
                            </xsl:call-template>
                </font>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template name="tokenize">
          <xsl:param name="string" />
          <xsl:param name="delim" />
    
          <xsl:choose>
            <xsl:when test="contains($string, $delim)">
              <xsl:element name="{substring-before($string,$delim)}">
                <xsl:call-template name="tokenize">
                <xsl:with-param name="string" select="substring-after($string, $delim)" />
                <xsl:with-param name="delim" select="$delim" />
              </xsl:call-template>
              </xsl:element>
            </xsl:when>
            <xsl:otherwise>
              <xsl:element name="{$string}">
                <xsl:apply-templates/>
              </xsl:element>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:template>
    
    </xsl:stylesheet>
    

    它处理@face 内的任意数量的令牌。例如,当应用于以下输入时:

    <?xml version="1.0"?>
    <root>
        <para font="Times" size="12" face="i b u s">This is some text.</para>
        <para font="Times" size="12" face="i b">This is some more text.</para>
    </root>
    

    结果是:

    <root>
       <para>
          <font name="Times" size="12">
             <i>
                <b>
                   <u>
                      <s>This is some text.</s>
                   </u>
                </b>
             </i>
          </font>
       </para>
       <para>
          <font name="Times" size="12">
             <i>
                <b>This is some more text.</b>
             </i>
          </font>
       </para>
    </root>
    

    【讨论】:

      【解决方案3】:

      我编写了一个样式表,其中包含一个表格,您可以在其中添加样式并将它们与标签相关联。例如,您可以将&lt;b&gt; 替换为&lt;strong&gt; 或添加新的&lt;tag name="strike" style="Strike-through"/&gt;。该表可通过$tags 变量访问,用于选择与face 属性样式名称对应的标记。

      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
          xmlns:my="my-namespace" exclude-result-prefixes="my" version="1.0">
      
          <xsl:output indent="yes"/>
          <xsl:strip-space elements="*"/>
      
          <my:styles>
              <tag name="b" style="Bold"/>
              <tag name="i" style="Italic"/>
              <tag name="u" style="Underline"/>
          </my:styles>
      
          <xsl:variable name="tags" select="document('')//tag" />
      
          <xsl:template match="/">
              <xsl:apply-templates select="document/para" mode="initial" />
          </xsl:template>
      
          <xsl:template match="para" mode="initial">
              <xsl:copy>
                  <font name="{@font}" size="{@size}">
                      <xsl:choose>
                          <xsl:when test="@face">
                              <xsl:call-template name="add-style">
                                  <xsl:with-param name="styles" select="concat(@face,' ')"/>
                              </xsl:call-template>
                          </xsl:when>
                          <xsl:otherwise>
                              <xsl:value-of select="."/>
                          </xsl:otherwise>
                      </xsl:choose>
                  </font>
              </xsl:copy>
          </xsl:template>
      
          <xsl:template name="add-style">
              <xsl:param name="styles"/>
              <xsl:param name="current-style" select="substring-before($styles, ' ')"/>
              <xsl:message>value:<xsl:value-of select="$styles"/>:</xsl:message>
              <xsl:if test="$current-style">
                  <xsl:element name="{$tags[@style = $current-style]/@name}">
                      <xsl:call-template name="add-style">
                          <xsl:with-param name="styles" select="substring-after($styles, ' ')"/>
                      </xsl:call-template>
                      <xsl:if test="not(contains(substring-after($styles, ' '), ' '))">
                          <xsl:value-of select="." />
                      </xsl:if>
                  </xsl:element>
              </xsl:if>
          </xsl:template>
      
      </xsl:stylesheet>
      

      如果你使用这个作为输入:

      <document>
          <para font="Times" size="12" face="Bold Italic">This is some text.</para>
          <para font="Times" size="12" face="Bold">This is some more text.</para>
          <para font="Times" size="12" face="Italic Bold Underline">This is some text 3.</para>
          <para font="Times" size="12" face="Italic">This is some more text 4.</para>
          <para font="Times" size="12">This is some more text 4.</para>
      </document>
      

      你得到这个结果:

      <para>
         <font name="Times" size="12">
            <b>
               <i>This is some text.</i>
            </b>
         </font>
      </para>
      <para>
         <font name="Times" size="12">
            <b>This is some more text.</b>
         </font>
      </para>
      <para>
         <font name="Times" size="12">
            <i>
               <b>
                  <u>This is some text 3.</u>
               </b>
            </i>
         </font>
      </para>
      <para>
         <font name="Times" size="12">
            <i>This is some more text 4.</i>
         </font>
      </para>
      <para>
         <font name="Times" size="12">This is some more text 4.</font>
      </para>
      

      我没有使用大选择,但我不得不使用一个来解决face 属性可能不存在的情况。

      如果face 属性中有多余的空格,它将失败,因为我使用空格(甚至添加一个)来控制递归。但这可以通过normalize-space() 解决。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-17
        • 2023-03-23
        相关资源
        最近更新 更多