如何使用xsl

时间:2018-04-08 12:12:02

标签: xml xslt directed-acyclic-graphs

我简化了一个代表有向无环图的xml文件:

<?xml version="1.0" encoding="utf-8"?>
<items>
  <item id="1">
  </item>
  <item id="2">
    <parent idref="1" />
  </item>
  <item id="3">
    <parent idref="1" />
  </item>
  <item id="4">
    <parent idref="3" />
  </item>
  <item id="5">
    <parent idref="4" />
  </item>
  <item id="6">
    <parent idref="3" />
  </item>
  <item id="7">
    <parent idref="4" />
  </item>
  <item id="8">
    <parent idref="4" />
  </item>
  <item id="9">
    <parent idref="4" />
  </item>
  <item id="10">
    <parent idref="6" />
  </item>
</items>

这种表示允许无限深度(我不知道数学术语)。每个项目都有一个且只有一个父项,但根项目除外,它没有父项。每个项目可以包含0到任意数量的子项目。每个项目都由其id标识,这是一个任意标签。

以点表示法可视化图形可能更容易。在我的文件中,更容易看到项目3有两个子节点,项目4有4:

digraph
{
    1 -> {2, 3}
    3 -> {4, 6}
    4 -> {5, 7, 8, 9}
    6 -> 10
}

计算第3项的子项数是:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
    <xsl:value-of select="count(items/item/parent[@idref='3'])" />
</xsl:template>

</xsl:stylesheet>

xslt样式表输出2,这是正确的。如果我将3更改为4,则输出为4,这也是正确的。

问题:我想计算特定项目的所有子项。

对于第3项,正确答案是7(项目4,6,5,7,8,9,10)。对于第1项,答案是9.对于第7项,它是0.我怀疑答案涉及递归,我已成功用于构建树的部分,但不传递值或计算总和。

2 个答案:

答案 0 :(得分:3)

对于以下交叉引用,我建议设置密钥然后,为了解决问题,递归是解决方案的另一个关键,所以在XSLT 2或3中你可以用递归函数做到:

(********************************************)
(* Extra example: a parameterized signature *)
(*                                          *)
(* It can be noted that this feature of Coq *)
(* module types has no equivalent in OCaml… *)
(********************************************)
Module Type MT2 (M : MT).
  Parameter u : M.t.
  Import M. (* => we can now write t rather than M.t *)
  Parameter f : t -> t.
End MT2.

(* Another parameterized module *)
Module F2 (M : MT) (M2 : MT2 M).
  Import M2.
  Definition fu := f u.
End F2.

(* Modules implementing MT and MT2 *)
Module M <: MT.
  Definition t := bool.
End M.

Module M2 <: MT2 M.
  Definition u := false.
  Definition f := negb.
End M2.

(* Instantiation *)
Module FM2 := F2 M M2.
Compute FM2.fu.

https://xsltfiddle.liberty-development.net/bdxtpH/0https://xsltfiddle.liberty-development.net/bdxtpH/1以及https://xsltfiddle.liberty-development.net/bdxtpH/2就是一些例子。

对于XSLT 2,您可以使用本地<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mf="http://example.com/mf" exclude-result-prefixes="xs mf" version="3.0"> <xsl:param name="start-id">7</xsl:param> <xsl:key name="id" match="item" use="@id"/> <xsl:key name="child" match="item" use="parent/@idref"/> <xsl:function name="mf:descendants" as="element(item)*"> <xsl:param name="item" as="element(item)*"/> <xsl:sequence select="let $children := key('child', $item/@id, root($item)) return ($children | $children!mf:descendants(.))"/> </xsl:function> <xsl:template match="/"> <xsl:value-of select="count(mf:descendants(key('id', $start-id)))"/> </xsl:template> </xsl:stylesheet> 代替上面使用的xsl:variable

let $children

http://xsltransform.hikmatu.com/pPgCcou

对于XSLT 1,您可以使用稍微不同的方法,使用递归的命名模板收集后代,直到找不到更多子项,然后输出计数:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="xs mf">

  <xsl:param name="start-id">1</xsl:param>

  <xsl:key name="id" match="item" use="@id"/>
  <xsl:key name="child" match="item" use="parent/@idref"/>

  <xsl:function name="mf:descendants" as="element(item)*">
      <xsl:param name="item" as="element(item)*"/>
      <xsl:variable name="children" select="key('child', $item/@id, root($item))"/>
      <xsl:sequence 
        select="$children | $children/mf:descendants(.)"/>
  </xsl:function>

  <xsl:template match="/">
      <xsl:value-of select="count(mf:descendants(key('id', $start-id)))"/>
  </xsl:template>

</xsl:transform>

https://xsltfiddle.liberty-development.net/3NzcBsK/0https://xsltfiddle.liberty-development.net/3NzcBsK/1https://xsltfiddle.liberty-development.net/3NzcBsK/2有样本数据。

答案 1 :(得分:0)

只是为了变化,这是另一种方法:

<xsl:param name="start-id">7</xsl:param>

<xsl:key name="id" match="item" use="@id"/>

<xsl:function name="f:has-ancestor" as="xs:boolean">
  <xsl:param name="d" as="item"/>
  <xsl:param name="a" as="item"/>
  <xsl:sequence select="exists($d) and ($a is $d or 
       f:has-ancestor(key('id', $d/parent/@idref), $a))"/>
</xsl:function>

然后

select="count(//item[f:has-ancestor(., key('id', $start-id))])"/>

(当然,哪一种更快取决于你树木的浓密感。)