XSLT <密钥使用= "position()" ../> 问题



我有一个(非常excel电子表格像..)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>

我想用一种"适当"/"有效"的方式来做这件事,这就是为什么我找你们来帮我。
我认为使用key来匹配data位置到header位置将是我的解决方案,但由于某种原因它只是不起作用(我已经~X()。

我需要的是指出我的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

谁能给我指个正确的方向?

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

因此,根据您的输出,看起来每个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(),在变量的选择中,当前节点列表由作为正在处理的row元素的子元素的所有data元素组成。因此,position()产生当前节点 (data元素)在该列表中的位置。该值存储在$posn变量中。

key('header',1)产生一个包含所有header元素的节点列表,原因如上所述:每个header的键值总是1。因此,key('header',1)[$posn]给出了n第一个头元素,其中n是当前数据元素在其兄弟元素中的位置。

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

现在我们深入一点…谓词本质上是布尔型的,但是当谓词中的表达式e是数字时,它被视为position() = e的缩写。第二个元素名表达式等于

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

position() = position()对于任何给定的上下文总是为真,所以上面的等价于

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

是所有头信息的列表

从http://www.w3.org/TR/xslt #键

因此,节点x有一个带有name的键y和value z当且仅当存在以下xsl:key元素:
  • x匹配指定的模式的match属性xsl:key元素;

  • name属性的值xsl:key元素的值等于 y ;和

  • 中指定的表达式
  • xsl:keyuse属性元素以x作为当前节点和节点列表只包含x作为当前导致对象u的节点列表,那么z等于结果将u转换为字符串,如by调用string函数,或者u是一个节点集,z等于一个或多个u.

表示use属性中的position()始终为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>
输出:

<morning>
    <row>
        <line>2</line>
        <product>HU12_SETUP</product>
        <order>16069061</order>
        <qty>1</qty>
        <deadline>2011-04-13T09:22:59.980</deadline>
    </row>
    <row>
        <line>1</line>
        <product>40PFL7605H/12</product>
        <order>16310360</order>
        <qty>200</qty>
        <deadline>2011-04-13T09:22:59.980</deadline>
    </row>
</morning>

最新更新