在XSLT变量中构造一系列XML元素

时间:2012-07-18 02:00:56

标签: xslt xpath xslt-2.0

要说清楚,我已经找到了一种方法来完成我想要使用XSLT的东西,但它让我感到非常低效,我想看看是否有可能使用不同的解决方案,因为它有助于写作未来的样式表。

另外,我真诚地为以下段落的冗长道歉。

最低限度,我试图从一个用俄语编写的故事中提取并用XML编码('pavlova.xml' - 遗憾的是文件太大而无法发布我的问题,但这并不是完全必要的)数据会用于在SVG中生成饼图。我试图将这些数据存储在一个名为$ chartData的变量中。给定的饼图将总体上与目标字符(参数)有关,并且饼图的每个单独的块将表示说话者(另一个字符),它们的大小指示每个说话者相对于其他说话者说出目标字符的多少。我开始迭代所有扬声器(任何曾经提到过目标角色的角色),然后确定角色的名字在特定演讲者的演讲中出现的次数。但是,这些值仅对于说出角色的总和次数有用。我需要总计来计算生成饼图的百分比。我知道我能够在另一个变量中单独计算这个,但我有兴趣看看我是否可以在变量中存储一系列节点。单独计算总和似乎是浪费的,因为它遍历的路径几乎与找到单个计数的路径相同,在检索单个值之后对其进行求和会更有效。到目前为止,我已经能够计算出我想要的一切,但我正在尝试使用xsl:for-each迭代变量并让它将我的变量识别为序列。

我希望变量的格式为:

<count name="$speaker1>
    <xsl:value-of select="$spokenCount1"/>
</count>
<count name="$speaker2>
    <xsl:value-of select="$spokenCount2"/>
</count>
...

我已经尝试了很多解决这个问题的方法,我不确定是否可以解决。首先是使用@as属性更准确地定义xsl:variable的内容(具体来说,尝试将其内容定义为序列),但我发现很难找到并理解该属性的文档以及SequenceType的工作原理。其次,我尝试了如何将内容放入变量中(直接将for-each放入变量中并使用xsl:value-of或xsl:copy-of,而不是声明变量并使用xsl:copy-在外部for-each中将数据附加到变量上。)

似乎xsl:copy-of,而不是xsl:sequence(具有讽刺意味)是我需要附加一系列节点。我只能通过以下代码获得上面描述的格式:

<xsl:variable name="chartData"/>
    <xsl:for-each
        select="distinct-values(//speech[.//name[not(parent::nonspeech) and ./@ref eq $character]]/@speaker)">
        <xsl:copy-of select="$chartData"/>
        <xsl:variable name="speaker" select="current()"/>
        <xsl:variable name="spokenCount"
            select="document('pavlova.xml')/count(//speech[@speaker eq $speaker]//name[not(parent::nonspeech) and ./@ref eq $character])"/>
        <count name="{$speaker}">
            <xsl:value-of select="$spokenCount"/>
        </count>
    </xsl:for-each>
    <xsl:variable name="spokenTotal" select="sum($chartData)"/>
    <xsl:value-of select="$chartData"/>
    <!--xsl:for-each select="$chartData/count">
        ...
    </xsl:for-each-->

最终目标是使每个循环的注释for-each循环遍历每个count元素,而不是给我一个错误。我的问题是,我可以将变量视为XML并迭代每个count元素,如果它看起来如下,或者它总是被视为字符串吗?

<?xml version="1.0" encoding="UTF-8"?>
<count name="young-man">4</count>
<count name="olga">24</count>
<count name="cecilija">34</count>
<count name="blonde">1</count>
<count name="vera">32</count>
<count name="servant-fem">6</count>
<count name="valickaja">20</count>
<count name="muse">66</count>
<count name="viktor">4</count>
<count name="dmitrij">15</count>
<count name="anna">11</count>
<count name="iličev">3</count>
<count name="narod">1</count>
<count name="society-man">3</count>

如果是这样,怎么样?

另外,如果有人可以解释,为什么xsl:copy-of-deep-copy只有在变量范围之外的for-each时才设置选择'$ chartData'?如果我尝试在变量中使用xsl:copy-of,它只复制文字<count>元素的文本内容并创建一个字符串:424341326206641511313)。

1 个答案:

答案 0 :(得分:1)

我担心你的问题很混乱。你要做的并不困难,但很难知道从哪里开始帮助你前进。

  

我有兴趣看看我是否可以在变量中存储一系列节点

是的,你可以。您需要清楚自己是否要创建新节点,或者是否希望变量保存对现有节点的引用。在第一种情况下,您使用“临时树”构造:

<xsl:variable name="tree">
  ... content ...
</xsl:variable>

在第二种情况下,您可以使用select属性,也可以使用包含的序列构造函数和“as”属性:

<xsl:variable name="seq" select="expression"/>

<xsl:variable name="seq" as="node()*">
  ... content ...
</xsl:variable>
  

我正在尝试使用xsl:for-each迭代变量并让它将我的变量识别为序列

嗯,每个变量的值都是一个序列,问题是,序列中的内容是什么?如果使用上面第一种形式的变量(content,no as attribute),则序列将是包含单个文档节点的单例序列;您可能希望使用路径表达式导航到其子项,例如<xsl:for-each select="$tree/*"/>。在第二种和第三种情况下,序列通常包含多个节点,这些节点可以是任何类型的节点,因此您更有可能编写<xsl:for-each select="$seq"/>

  

我发现很难找到并理解该属性的文档以及SequenceType的工作方式

你在哪里看?我不能推荐我的书“XSLT 2.0 Programmer's Reference”太高了...... 我认为你是那种想要理解事物真正起作用的人,而不是通过例子中的反复试验来学习。这本书是为那种人写的。

  

似乎xsl:copy-of,而不是xsl:sequence(具有讽刺意味)是我需要附加一系列节点。

不,你错了。如果要创建包含对现有节点的引用的变量,则需要xsl:sequence。如果要创建一个包含节点副本的临时树,则xsl:sequence和xsl:copy-of具有完全相同的效果 - 两者都将复制所选节点。

  

我可以将变量视为XML并迭代每个count元素,如果它看起来如下

是的,你可以。但是我们无法从您对序列的表示中看出这些节点是否是文档节点的子节点,并且知道这是至关重要的。

  

为什么xsl:copy-of-deep-copy只有在变量范围之外的for-each时才设置选择'$ chartData'?如果我尝试在变量中使用xsl:copy-of,它只复制文字元素的文本内容并创建一个字符串

你在这里得出了一个不正确的推论。 xsl:copy-of总是执行深层复制。如果您只看到节点的字符串内容,那是因为正在使用后续操作来雾化节点或提取其字符串值。