xslt group by - php


xslt group by - php

嘿,我有一个简单的xml文件:

<?xml version="1.0"?>
 <accidents>
 <accident>
  <org>1</org>
  <com>194</com>
  <dep>010</dep>
  <grav>0.64</grav>
 </accident>
 <accident>
  <org>1</org>
  <com>194</com>
  <dep>420</dep>
  <grav>0.54</grav>
 </accident>
 <accident>
  <org>1</org>
  <com>44</com>
  <dep>010</dep>
  <grav>0.4</grav>
 </accident>
</accidents>

我想应用xslt 1.0来计算dep的事故数量:输出应该是这样的:

dep 010:2起事故;dep 420:1起事故

谢谢,注意我使用php,所以saxon不能使用

此转换

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>
 <xsl:strip-space elements="*"/>
 <xsl:key name="kAccByDept" match="accident" use="dep"/>
 <xsl:template match=
  "accident
    [generate-id()
    =
     generate-id(key('kAccByDept', dep)[1])
     ]">
     <xsl:if test="position() > 1"> ; </xsl:if>
     <xsl:value-of select=
      "concat('dep ', dep, ' : ',
              count(key('kAccByDept', dep)), ' accidents')
      "/>
 </xsl:template>
 <xsl:template match="text()"/>
</xsl:stylesheet>

应用于所提供的XML文档时

<accidents>
    <accident>
        <org>1</org>
        <com>194</com>
        <dep>010</dep>
        <grav>0.64</grav>
    </accident>
    <accident>
        <org>1</org>
        <com>194</com>
        <dep>420</dep>
        <grav>0.54</grav>
    </accident>
    <accident>
        <org>1</org>
        <com>44</com>
        <dep>010</dep>
        <grav>0.4</grav>
    </accident>
</accidents>

生成所需的正确结果

dep 010 : 2 accidents ; dep 420 : 1 accidents

解释

  1. 分组的Muenchian方法

  2. 推送样式的XSLT处理

更新:OP没有真正的XSLT处理器——他的工具没有实现xsl:key

在这种情况下,以下解决方案(注意,这比Muenchian分组效率低得多)仍然解决了问题,产生了确切的所需输出

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output method="text"/>
     <xsl:template match=
      "accident
        [not(dep
            =
             preceding-sibling::*/dep
             )
        ]
      ">
         <xsl:value-of select=
          "concat('dep ', dep, ' : ',
                  count(following-sibling::*[dep = current()/dep]) +1,
                  ' accidents')
          "/>
         <xsl:if test=
         "following-sibling::*/dep
              [not(. = current()/dep)
              and
               not(.
            =
             ../preceding-sibling::*/dep)
              ]"> ; </xsl:if>
     </xsl:template>
     <xsl:template match="text()"/>
</xsl:stylesheet>

当应用于相同的XML文档(如上)时,会产生相同的正确结果:

dep 010 : 2 accidents ; dep 420 : 1 accidents

不确定为什么要使用XSLT,但如果必须这样做,您可以更容易地从PHP值创建dom元素:

$dom = new DOMDocument;
$dom->loadXML(file_get_contents('accidents.xml'));
$xpath = new DOMXPath($dom);
$deps = array();
foreach ($xpath->query('/accidents/accident/dep') as $node) {
   $n = $node->nodeValue;
   if (!isset($deps[$n])) {
      $deps[$n] = 0;
   }
   $deps[$n]++;
}

这给出了一个以"dep"为键、以事故编号为值的数组。

使用:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:key name="k" match="accident" use="dep"/>
  <xsl:template match="/accidents">
    <xsl:apply-templates select="accident[generate-id() = generate-id(key('k', dep))]"/>
  </xsl:template>
  <xsl:template match="accident">
    <xsl:value-of select="concat('dep ', dep, ' : ', count(key('k', dep)), ' accidents; ')"/>
  </xsl:template>
</xsl:stylesheet>

无Muenchian方法的解决方案:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:template match="/accidents">
    <xsl:apply-templates select="accident[not(preceding-sibling::accident/dep = dep)]"/>
  </xsl:template>
  <xsl:template match="accident">
    <xsl:value-of select="concat('dep ', dep, ' : ', count(../accident[dep = current()/dep]), ' accidents; ')"/>
  </xsl:template>
</xsl:stylesheet>

输出:

dep 010 : 2 accidents; dep 420 : 1 accidents;

我不得不承认,我不确定为什么使用密钥的解决方案不适合你,但这里有一个不适合,它可能是一个变通方法:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:template match="/">
    <xsl:variable name="list">
      <xsl:apply-templates />
    </xsl:variable>
    <xsl:value-of select="substring($list,1,string-length($list)-2)" />
  </xsl:template>
  <xsl:template match="accident">
    <xsl:if test="not(preceding-sibling::accident[dep = current()/dep])">
      <xsl:value-of select="concat('dep ',dep,' : ',count(. | following-sibling::accident[dep = current()/dep]),' accidents ;')" />
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

它只在accident元素之前没有具有相同dep的同级元素的情况下输出,对其自身和具有相同dep的所有后续accident元素进行计数。它将结果分配给一个变量,然后使用子字符串截断尾部的;。如果这实际上不是一个问题,那么可以很容易地完全忽略match="/"模板。