XSLT 1.0:从外部文档中提取顺序信息 [英] XSLT 1.0: extract sequential infromation from external document

查看:24
本文介绍了XSLT 1.0:从外部文档中提取顺序信息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 xslt 执行 xml 到 xml 的转换,这样每个元素都有一个 GUID,按顺序从外部 xml 文档中提取.

源 xml:

<数据集><data name="foo"/><data name="bar"/>...</数据集>

ID 列表:

<ids><id>some-GUID</id><id>another-GUID</id>...</ids>

所需的输出:

<数据集><data name="foo" id="some-GUID"/><data name="bar" id="another-GUID"/>...</数据集>

但我每次都得到相同的第一个 ID:

<数据集><data name="foo" id="some-GUID"/><data name="bar" id="some-GUID"/>...</数据集>

到目前为止,这是我得到的 xsl:

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:template match="/|node()|@*"><xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy></xsl:模板><xsl:template match="dataSets"><xsl:for-each select="data"><xsl:variable name="c"><xsl:number value="count(preceding-sibling::*|self::*)"/></xsl:变量><xsl:copy><xsl:attribute name="id"><xsl:value-of select="document('idList.xml')/ids/id[$c]"/></xsl:attribute><xsl:apply-templates select="node()|@*"/></xsl:copy></xsl:for-each></xsl:模板></xsl:transform>

我尝试添加 <xsl:attribute name="num"><xsl:value-of select="$c"/></xsl:attribute>到 xsl 以查看每次迭代时变量是什么,它从 1 开始,每次通过 for-each 递增,正如我所期望的那样,所以我不知道为什么它不起作用.
任何帮助将不胜感激.

解决方案

怎么样:

XSLT 1.0

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/><xsl:strip-space elements="*"/><!-- 身份变换--><xsl:template match="@*|node()"><xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy></xsl:模板><xsl:template match="数据"><xsl:copy><xsl:copy-of select="@*"/><xsl:variable name="i" select="count(preceding::data) + 1"/><xsl:attribute name="id"><xsl:value-of select="document('idList.xml')/ids/id[$i]"/></xsl:attribute></xsl:copy></xsl:模板></xsl:stylesheet>

注意:

  1. 如果所有 data 元素都是同一个 dataSets 父元素下的兄弟元素,你可以通过只计算前面的兄弟元素来加快速度:

  2. 可能有更好的方法将 GUID 分配给节点.

<小时>

补充说明:

您的尝试不起作用的原因是两个因素的组合:

首先,定义变量的方式:

<xsl:number value="count(preceding-sibling::*|self::*)"/></xsl:变量>

导致变量成为结果树片段.该变量包含单个根节点,xsl:number 生成的字符串是该节点的子节点.

接下来,您尝试将该变量用作表达式中的数字谓词:

然而,由于变量不是一个数字,它被评估为一个布尔值 - 并且非空返回true,导致all id 节点以通过测试(当然,在 XSLT 1.0 中 xsl:value 将只返回其中第一个的值).

如何解决:

您可以通过三种方法解决此问题:

  1. 不要将变量用作数字谓词.相反,将它放在一个表达式中,将其与位置显式进行比较(如@potame 的回答中所建议的那样):

  2. 在将其用作谓词之前将其转换为数字:

  3. 通过使用 select 属性定义变量,从一开始就消除问题,这将导致变量成为以数字开头的数字:

    <xsl:variable name="c" select="count(preceding-sibling::*|self::*)"/>

另见:
http://www.w3.org/TR/xslt/#variable-values
http://www.w3.org/TR/xpath/#predicates>

I am trying to use xslt to perform an xml to xml transform, such that each element is given a GUID, pulled in sequential order from an external xml document.

Source xml:

<?xml version="1.0"?>
<dataSets>
  <data name="foo"/>
  <data name="bar"/>
  ...
</dataSets>

List of IDs:

<?xml version="1.0"?>
<ids>
  <id>some-GUID</id>
  <id>another-GUID</id>
  ...
</ids>

Desired output:

<?xml version="1.0"?>
<dataSets>
  <data name="foo" id="some-GUID"/>
  <data name="bar" id="another-GUID"/>
  ...
</dataSets>

But I'm just getting the same, first ID each time:

<?xml version="1.0"?>
<dataSets>
  <data name="foo" id="some-GUID"/>
  <data name="bar" id="some-GUID"/>
  ...
</dataSets>

So far this is the xsl I've got:

<?xml version="1.0"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/|node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="dataSets">
    <xsl:for-each select="data">
      <xsl:variable name="c">
        <xsl:number value="count(preceding-sibling::*|self::*)"/>
      </xsl:variable>

      <xsl:copy>
        <xsl:attribute name="id">
          <xsl:value-of select="document('idList.xml')/ids/id[$c]"/>
        </xsl:attribute>
        <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>

    </xsl:for-each>
  </xsl:template>

</xsl:transform>

I've tried adding <xsl:attribute name="num"><xsl:value-of select="$c"/></xsl:attribute> to the xsl to see what the variable is each time it iterates and it starts at 1 and increments each time through the for-each just as I'd expect so I've no idea why it's not working.
Any help would be appreciated.

解决方案

How about:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="data">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:variable name="i" select="count(preceding::data) + 1" />
        <xsl:attribute name="id">
            <xsl:value-of select="document('idList.xml')/ids/id[$i]"/>
        </xsl:attribute>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Note:

  1. If all data elements are siblings under the same dataSets parent, you can expedite this by counting only the preceding siblings:

    <xsl:variable name="i" select="count(preceding-sibling::data) + 1" />
    

  2. There are probably better ways to assign GUIDs to nodes.


Addditional explanation:

The reason why your attempt does not work is a combination of two factors:

First, the way you define your variable:

<xsl:variable name="c">
   <xsl:number value="count(preceding-sibling::*|self::*)"/>
</xsl:variable>

results in the variable being a result tree fragment. The variable contains a single root node, and the string generated by xsl:number is a child of that node.

Next, you try to use the variable as a numeric predicate in the expression:

<xsl:value-of select="document('idList.xml')/ids/id[$c]"/>

However, since the variable is not a number, it is evaluated as a boolean - and being non-empty it returns true, causing all id nodes to pass the test (and, of course, in XSLT 1.0 xsl:value will only return the value of the first one of these).

How to fix it:

There are three ways you can fix this problem:

  1. Do not use the variable as a numeric predicate. Instead, put it in an expression that compares it to the position explicitly (as suggested in the answer by @potame):

    <xsl:value-of select="document('idList.xml')/ids/id[position()=$c]"/>
    

  2. Convert it to number before using it as a predicate:

    <xsl:value-of select="document('idList.xml')/ids/id[number($c)]"/>
    

  3. Eliminate the problem right from the start by defining the variable using the select attribute, which will cause the variable to become a number to begin with:

    <xsl:variable name="c" select="count(preceding-sibling::*|self::*)" />
    

See also:
http://www.w3.org/TR/xslt/#variable-values
http://www.w3.org/TR/xpath/#predicates

这篇关于XSLT 1.0:从外部文档中提取顺序信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆