xslt <key use =“position()”.. =“”>问题</key>

时间:2011-05-03 19:06:44

标签: xslt

我有一个(非常优秀的电子表格,如...)xml数据,我应该从中创建一个更具可读性的数据。
我在结构顶部有标题,并希望从文本值创建elements并将其应用于文档的其余部分。

可能实际数据说得更清楚,所以我的输入文档看起来像

<?xml version="1.0"?>
<root>
    <headers>
        <header>line</header>
        <header>product</header>
        <header>order</header>
        <header>qty</header>
        <header>deadline</header>
    </headers>
    <row>
        <data>2</data>
        <data>HU12_SETUP</data>
        <data>16069061</data>
        <data>1</data>
        <data>2011-04-13T09:22:59.980</data>
    </row>
    <row>
        <data>1</data>
        <data>40PFL7605H/12</data>
        <data>16310360</data>
        <data>200</data>
        <data>2011-04-13T09:22:59.980</data>
    </row>
</root>

我的目标是将xml文档设为:

<?xml version="1.0"?>
<morning>
    <row>
        <line>2</line>
        <product>HU12_SETUP</product>
        <order>16069061</order>
        <qty>1</qty>
        <deadline>0</deadline>
    </row>
    <row>
        <line>1</line>
        <product>40PFL7605H/12</product>
        <order>16310360</order>
        <qty>200</qty>
        <deadline>77</deadline>
    </row>
</morning>

我想以“正确”/“有效”的方式做到这一点,这就是为什么我转向你们,伙计们,帮助我。
我认为使用keydata位置与header位置匹配将是我的解决方案,但由于某种原因它只是不起作用(我已经是~X()。< / p>

我需要指出我的xsl有什么问题,和/或如果key概念有问题,建议我更好的解决方案。

这是我的(调试)xsl:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:key name="header" match="header" use="position()" />

    <xsl:template match="/">
        <morning>
            <xsl:apply-templates />
        </morning>
    </xsl:template>
    <xsl:template match="headers" />
    <xsl:template match="row">
        <xsl:copy>
            <xsl:apply-templates />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="data">
        <xsl:element name="{concat('bla-',position())}">
            <xsl:value-of select="key('header',position())" />
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

我验证position()实际上是否正确。

我的输出因我使用的样式表版本而异。

输出1.0

<?xml version='1.0' encoding='UTF-8' ?>
<morning>
  <row>
    <bla-1>line</bla-1>
    <bla-2/>
    <bla-3/>
    <bla-4/>
    <bla-5/>
  </row>
  <row>
    <bla-1>line</bla-1>
    <bla-2/>
    <bla-3/>
    <bla-4/>
    <bla-5/>
  </row>
</morning>

输出2.0

<?xml version='1.0' encoding='UTF-8' ?>
<morning>
  <row>
    <bla-1>line product order qty deadline</bla-1>
    <bla-2/>
    <bla-3/>
    <bla-4/>
    <bla-5/>
  </row>
  <row>
    <bla-1>line product order qty deadline</bla-1>
    <bla-2/>
    <bla-3/>
    <bla-4/>
    <bla-5/>
  </row>
</morning>

正如你所看到的,key('header',position())在除了第一个之外的所有情况下都给了我空字符串(这就是为什么我把它作为值,而不是元素名称)。

我将不胜感激,谢谢你们!

添加
基于@ LarsH和@Alejandro的回答(仍然坚持这个key的事情......)我想出了:

<xsl:template match="data">
    <xsl:variable name="posn" select="position()" />
    <xsl:element name="{key('header',1)[$posn]}" />
    <xsl:element name="{key('header',1)[position()]}" />
</xsl:template>

我可以看到在这里使用带有静态1的键是愚蠢的,但是我害怕为什么那里的两个结果元素不匹配?
第一个是正确的,第二个总是给我line,分别是lineproductorderqtydeadline

有人能指出我正确的方向吗?

2 个答案:

答案 0 :(得分:3)

XSLT 1.0和2.0之间的输出差异在1.0中,xsl:value-of输出所选节点集中第一个节点的字符串值,而在2.0中输出(IIRC)输出所选节点集中所有节点的连接值,空格为默认分隔符。 (在你相信我之前检查一下。)

因此,根据您的输出,看起来每个header元素的键值始终为1.我不确定在position()属性中使用时use会产生什么一把钥匙(它取决于我没有抬头看的上下文),这对我来说是合理的。

我不会使用position()的密钥,而是尝试类似:

 <xsl:key name="header" match="header"
          use="count(preceding-sibling::header) + 1" />

这很有效。如果你有数千个标题可能会很慢,但我认为你永远不会。

或者,您可以决定不使用密钥,而是

<xsl:template match="data">
    <xsl:variable name="posn" value="position()" />
    <xsl:element name="{concat('bla-', $posn)}">
        <xsl:value-of select="/root/headers/header[$posn]" />
    </xsl:element>
</xsl:template>

通常使用密钥可以获得更好的性能,但在这种情况下,似乎不清楚使用[$posn]谓词会有明显的性能优势。

<强>增加:

上面“添加”的答案足够长,需要在这里而不是评论。在你的补充:

<xsl:template match="data">
    <xsl:variable name="posn" select="position()" />
    <xsl:element name="{key('header',1)[$posn]}" />
    <xsl:element name="{key('header',1)[position()]}" />

如我的评论中所述,上下文包括当前节点当前节点列表。对于第一个position(),在选择变量时,当前节点列表由作为正在处理的data元素的子元素的所有row元素组成。因此position()会在该列表中生成当前节点data元素)的位置。该值存储在$posn变量中。

key('header',1)会生成所有header元素的节点列表,原因如上所述:每个header的键值始终为1.因此key('header',1)[$posn]给出{n第1}个标题元素,其中n是当前数据元素在其兄弟之间的位置。

对于第二个position()调用:在谓词中,通过将谓词单独应用于由XPath表达式生成的节点列表中的每个节点直到该点,确定上下文,并将该节点列表作为< em>当前节点列表。因此,在key('header',1)[...]的方括号内,上下文节点列表key('header',1)返回的节点列表。同样,这是所有header元素的列表。因此,对于每个header元素,position()此处返回该标头在其兄弟之间的位置。

现在我们得到更深一点......谓词本质上是布尔值,但是当谓词中的表达式e是数字时,它被视为position() = e的缩写。所以你的第二个元素名称表达式等同于

    <xsl:element name="{key('header',1)[position() = position()]}" />
对于任何给定的上下文,

position() = position()始终为true,因此上述内容等同于

    <xsl:element name="{key('header',1)}" />

这是所有标题的列表。

答案 1 :(得分:3)

来自http://www.w3.org/TR/xslt#key

  

因此,节点 x 具有名称的密钥    y 和值 z 当且仅当有xsl:key元素时:

     
      
  • x 匹配指定的模式   在match属性中   xsl:key元素;

  •   
  • name属性的值   xsl:key元素的等于   ý;以及

  •   
  • 当指定的表达式时   use的{​​{1}}属性   使用 x 评估元素   当前节点和节点列表   仅包含 x 作为当前   节点列表导致对象 u ,   然后 z 等于结果   将 u 转换为字符串,如同   调用xsl:key函数,或    u 是一个节点集, z 等于一个或多个的字符串值    u 中的节点。

  •   

这意味着string属性中的position()始终为use

此样式表:

1

输出:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:variable name="vHeaders" select="/root/headers/header"/>
    <xsl:template match="/">
        <morning>
            <xsl:apply-templates />
        </morning>
    </xsl:template>
    <xsl:template match="headers" />
    <xsl:template match="row">
        <xsl:copy>
            <xsl:apply-templates />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="data">
        <xsl:variable name="vPosition" select="position()"/>
        <xsl:element name="{$vHeaders[$vPosition]}">
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>