【问题标题】:How to use XSLT to get only certain rows and certain columns?如何使用 XSLT 仅获取某些行和某些列?
【发布时间】:2011-03-11 08:25:52
【问题描述】:

如何使用 XSLT 转换此 XML 文件:

<file>
    <row>
        <cell></cell>
        <cell>(info...)</cell>
        <cell></cell>
    </row>
    <row>
        <cell>first name</cell>
        <cell>last name</cell>
        <cell>age</cell>
    </row>
    <row>
        <cell>Jim</cell>
        <cell>Smith</cell>
        <cell>34</cell>
    </row>
    <row>
        <cell>Roy</cell>
        <cell>Rogers</cell>
        <cell>22</cell>
    </row>
    <row>
        <cell>Hank</cell>
        <cell>Grandier</cell>
        <cell>23</cell>
    </row>
    <row>
        <cell>(info...)</cell>
        <cell></cell>
        <cell>(info...)</cell>
    </row>

    <row>
        <cell>Sally</cell>
        <cell>Cloud</cell>
        <cell>26</cell>
    </row>

    <row>
        <cell>John</cell>
        <cell>Randall</cell>
        <cell>44</cell>
    </row>  

</file>

到这个 XML 文件:

<file>
    <row>
        <cell>Jim</cell>
        <cell>34</cell>
    </row>
    <row>
        <cell>Roy</cell>
        <cell>22</cell>
    </row>
    <row>
        <cell>Sally</cell>
        <cell>26</cell>
    </row>
    <row>
        <cell>John</cell>
        <cell>44</cell>
    </row>  
</file>

基本上规则是:

  • 第一列和第三列(名字和年龄)
  • 仅在特定范围内的行,例如在上面的简单示例中,它将是 rows 3-5rows 7-8 所以我假设我需要某种包含此信息的映射表李>

附录

这是我使用 MarcoS 关于参数的提示的解决方案:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

    <xsl:output method="xml" indent="yes" omit-xml-declaration="no" />

    <xsl:param name="range-1-begin"  select="3"/>
    <xsl:param name="range-1-end"  select="4"/>

    <xsl:param name="range-2-begin"  select="6"/>
    <xsl:param name="range-2-end"  select="7"/>

    <xsl:template match="file">
        <marco>
            <xsl:for-each select="row">
                <xsl:if test="(position() &gt;= $range-1-begin and position() &lt;= $range-1-end)
                    or (position() &gt;= $range-2-begin and position() &lt;= $range-2-end)">
                    <row>
                        <xsl:for-each select="cell">
                            <xsl:if test="position() = 1 or 
                                position() = 3">
                                <cell>
                                    <xsl:value-of select="."/>
                                </cell>
                            </xsl:if>
                        </xsl:for-each>
                    </row>
                </xsl:if>
            </xsl:for-each>
        </marco>
    </xsl:template>

</xsl:stylesheet>

【问题讨论】:

  • 好问题,+1。请参阅我的答案,以获得使用最基本 XSLT 设计模式之一的完整、简短和简单的解决方案——覆盖身份规则。 :)

标签: xml xslt


【解决方案1】:

这是一个可能的解决方案(可能不是很优雅):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

    <xsl:output method="xml" indent="yes" omit-xml-declaration="no" />

    <xsl:template match="file">
        <file>
            <xsl:for-each select="row">
                <xsl:if test="(position() >= 3 and position() &lt; 5)
                    or (position() >= 7 and position() &lt;= 8)">
                    <row>
                        <xsl:for-each select="cell">
                            <xsl:if test="position() = 1 or 
                                position() = 3">
                                <cell>
                                    <xsl:value-of select="."/>
                                </cell>
                            </xsl:if>
                        </xsl:for-each>
                    </row>
                </xsl:if>
            </xsl:for-each>
        </file>
    </xsl:template>

</xsl:stylesheet>

本质上,您可以使用 XPath position() 函数来选择您想要的 rowcell 元素的范围。

【讨论】:

  • +1:如果您没有更好的选择行/列的标准(例如,单元格上的有用属性或不同的元素名称),那么您必须这样做。
  • 效果很好,有什么办法可以将 3,5,7,8 放入“映射 xml”或其他一些数据存储中并使用 XSLT 读取它?
  • @Edward Tanguay:您可以使用&lt;xsl:param name="param-name-1"/&gt;,然后从调用 XSLT 转换的代码中注入参数值。这样的代码可以在任何你想要的地方获取参数值形式:mapping.xml、DB、GUI 等。要在 XPath 表达式中使用参数,只需记住在它们的名称前加上 $ 符号,例如 @ 987654327@
  • @MarcoS:不需要在属性值中编码&gt; 字符。内容模板内的位置测试仅在 上下文列表 位置很重要(推送样式、排序)时有用,否则在选择 XPath 表达式中使用它更直接。最后:“Brick”模板很糟糕
  • @Alejandro:关于&gt; 编码:你说得对:我已经编辑了上面的XSL。关于“内容模板内的位置测试”:你也是对的。我写道,这不是很优雅,但这是我想出的最快的解决方案。此外,在这种情况下,我不确定如何在选择 XPath 表达式中使用position()。能给个提示吗?
【解决方案2】:

这里有一段 XSLT 可以满足您的描述。

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="iso-8859-1" indent="yes" omit-xml-declaration="no"/>
  <xsl:template match="file">
    <xsl:copy>
      <xsl:apply-templates select="row[(position()&gt;=3 and position()&lt;=5) or (position()&gt;=7 and position()&lt;=8)]" />
    </xsl:copy>
  </xsl:template>
  <xsl:template match="row">
    <xsl:copy>
      <xsl:apply-templates select="cell[position()=1 or position()=3]" />
    </xsl:copy>
  </xsl:template>
  <xsl:template match="cell">
    <xsl:copy-of select="." />
  </xsl:template>
</xsl:stylesheet>

要在输出中选择您想要的行,我想我首先用一个可用作过滤器的属性来标记它们。 在调用 XSLT 的代码中,您可以在加载 XML 文档之后和应用转换之前使用 DOM 方法来执行此操作。例如保留吉姆史密斯但放弃罗伊罗杰斯:

<row keep="-1">
    <cell>Jim</cell>
    <cell>Smith</cell>
    <cell>34</cell>
</row>
<row>
    <cell>Roy</cell>
    <cell>Rogers</cell>
    <cell>22</cell>
</row>

并将 XSLT 中的行更改为:

<xsl:apply-templates select="row[@keep=-1]" />

【讨论】:

    【解决方案3】:

    这可能是最简单和最短的解决方案,也是基于使用和覆盖身份规则:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:my="my:my">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <my:params>
      <row-range start="3" end="5"/>
      <row-range start="7" end="8"/>
      <cell-positions>
       <pos>1</pos>
       <pos>3</pos>
      </cell-positions>
     </my:params>
    
     <xsl:template match="node()|@*">
         <xsl:copy>
           <xsl:apply-templates select="node()|@*"/>
         </xsl:copy>
     </xsl:template>
    
     <xsl:template match=
      "row[(not(position() >= document('')/*/my:params/row-range[1]/@start)
         or
           position() > document('')/*/my:params/row-range[1]/@end
           )
         and
          (not(position() >= document('')/*/my:params/row-range[2]/@start)
         or
           position() > document('')/*/my:params/row-range[2]/@end
           )
          ]
      "/>
    
     <xsl:template match=
      "cell[not(position()=document('')/*/my:params/cell-positions/*)]"/>
    </xsl:stylesheet>
    

    当此转换应用于提供的 XML 文档时:

    <file>
        <row>
            <cell></cell>
            <cell>(info...)</cell>
            <cell></cell>
        </row>
        <row>
            <cell>first name</cell>
            <cell>last name</cell>
            <cell>age</cell>
        </row>
        <row>
            <cell>Jim</cell>
            <cell>Smith</cell>
            <cell>34</cell>
        </row>
        <row>
            <cell>Roy</cell>
            <cell>Rogers</cell>
            <cell>22</cell>
        </row>
        <row>
            <cell>Hank</cell>
            <cell>Grandier</cell>
            <cell>23</cell>
        </row>
        <row>
            <cell>(info...)</cell>
            <cell></cell>
            <cell>(info...)</cell>
        </row>
        <row>
            <cell>Sally</cell>
            <cell>Cloud</cell>
            <cell>26</cell>
        </row>
        <row>
            <cell>John</cell>
            <cell>Randall</cell>
            <cell>44</cell>
        </row>
    </file>
    

    产生了想要的正确结果:

    <file>
       <row>
          <cell>Jim</cell>
          <cell>34</cell>
       </row>
       <row>
          <cell>Roy</cell>
          <cell>22</cell>
       </row>
       <row>
          <cell>Hank</cell>
          <cell>23</cell>
       </row>
       <row>
          <cell>Sally</cell>
          <cell>26</cell>
       </row>
       <row>
          <cell>John</cell>
          <cell>44</cell>
       </row>
    </file>
    

    【讨论】:

    • +1 好答案。在内容模板中进行验证,可以使用一般的document('')/*/my:params/row-range[$vPosition &gt;= @start and @end &gt;= $vPosition]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-24
    • 1970-01-01
    • 2012-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多