我有一个(非常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
概念有什么问题,建议我更好的解决方案。
<?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:key
的use
属性元素以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>