2010-09-16

XML 文書内に散らばる同名の要素を集めて数えたりする XSLT

=== XSLT ===
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:in="http://example.com/hack"
  xmlns="http://example.com/hack2"
  >

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/">
    <root>
      <xsl:call-template name="kvlistParser">
        <xsl:with-param name="kvlist">
          |uri=http://example.com/hack|ln=node1|param=string|
          |uri=http://example.com/hack|ln=node2|param=float|
          |uri=http://example.com/hack|ln=node3|param=date|
        </xsl:with-param>
      </xsl:call-template>
    </root>
  </xsl:template>

  <xsl:template name="kvlistParser">
    <xsl:param name="kvlist"/>
    <xsl:variable name="nkvl" select="normalize-space($kvlist)"/>
    <xsl:choose>
      <xsl:when test="contains($kvlist, ' ')">
        <xsl:call-template name="collectElems">
          <xsl:with-param name="keyval" select="substring-before($nkvl, ' ')"/>
        </xsl:call-template>
        <xsl:call-template name="kvlistParser">
          <xsl:with-param name="kvlist" select="substring-after($nkvl, ' ')"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="collectElems">
          <xsl:with-param name="keyval" select="$nkvl"/>
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template name="collectElems">
    <xsl:param name="keyval"/>
    <xsl:variable name="uri">
      <xsl:call-template name="keyvalGet">
        <xsl:with-param name="keyval" select="$keyval"/>
        <xsl:with-param name="key" select="'uri'"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="tagn">
      <xsl:call-template name="keyvalGet">
        <xsl:with-param name="keyval" select="$keyval"/>
        <xsl:with-param name="key" select="'ln'"/>
      </xsl:call-template>
    </xsl:variable>
    <class debug="{$keyval}">
      <tagname namespace="{$uri}">
        <xsl:value-of select="$tagn"/>
      </tagname>
      <count><xsl:value-of select="count(//*[local-name()=$tagn][namespace-uri()=$uri])"/></count>
      <xsl:for-each select="//*[local-name()=$tagn][namespace-uri()=$uri]">
        <leaf><xsl:value-of select="."/></leaf>
      </xsl:for-each>
    </class>
  </xsl:template>

  <xsl:template name="keyvalGet">
    <xsl:param name="keyval"/>
    <xsl:param name="key"/>
    <xsl:value-of
      select="substring-before(
      substring-after($keyval, concat('|', $key, '=')), '|')"
      />
  </xsl:template>

</xsl:stylesheet>
=== input ===
<?xml version="1.0"?>
<weirdPrefix:root
  xmlns:weirdPrefix="http://example.com/hack">
  <weirdPrefix:node2>val2a</weirdPrefix:node2>
  <weirdPrefix:node1>val1a</weirdPrefix:node1>
  <weirdPrefix:node2>val2b</weirdPrefix:node2>
  <weirdPrefix:node1>val1b</weirdPrefix:node1>
  <weirdPrefix:node1>val1c</weirdPrefix:node1>
  <weirdPrefix:node2>val2c</weirdPrefix:node2>
</weirdPrefix:root>
=== output ===
<?xml version="1.0"?>
<root xmlns="http://example.com/hack2" xmlns:in="http://example.com/hack">
  <class debug="|uri=http://example.com/hack|ln=node1|param=string|">
    <tagname namespace="http://example.com/hack">node1</tagname>
    <count>3</count>
    <leaf>val1a</leaf>
    <leaf>val1b</leaf>
    <leaf>val1c</leaf>
  </class>
  <class debug="|uri=http://example.com/hack|ln=node2|param=float|">
    <tagname namespace="http://example.com/hack">node2</tagname>
    <count>3</count>
    <leaf>val2a</leaf>
    <leaf>val2b</leaf>
    <leaf>val2c</leaf>
  </class>
  <class debug="|uri=http://example.com/hack|ln=node3|param=date|">
    <tagname namespace="http://example.com/hack">node3</tagname>
    <count>0</count>
  </class>
</root>