Using XSLT 2.0 to parse the values of multiple attributes into an array-like structure

纵然是瞬间 提交于 2019-12-05 18:07:53

I think what you're looking for is a sequence. A sequence can be either nodes or atomic values (see http://www.w3.org/TR/xslt20/#constructing-sequences).

Here's an example showing the construction of a sequence and then iterating over it. The sequence is the atomic values from @arch, but it could also be nodes.

XML Input

<doc>
    <foo arch="value1;value2;value3"/>
    <foo arch="value1:value4"/>
</doc>

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:variable name="archSequence" as="item()*">
        <xsl:for-each select="//@arch">
            <xsl:for-each select="tokenize(.,'[;:]')">
                <xsl:value-of select="."/>                  
            </xsl:for-each>             
        </xsl:for-each>         
    </xsl:variable>

    <xsl:template match="/*">
        <sequence>
            <xsl:for-each select="$archSequence">
                <item><xsl:value-of select="."/></item>
            </xsl:for-each>
        </sequence>
    </xsl:template>

</xsl:stylesheet>

XML Output

<sequence>
   <item>value1</item>
   <item>value2</item>
   <item>value3</item>
   <item>value1</item>
   <item>value4</item>
</sequence>

Example of a sequence of elements (same output):

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:variable name="archSequence" as="element()*">
        <xsl:for-each select="//@arch">
            <xsl:for-each select="tokenize(.,'[;:]')">
                <item><xsl:value-of select="."/></item>         
            </xsl:for-each>             
        </xsl:for-each>         
    </xsl:variable>

    <xsl:template match="/*">
        <sequence>
            <xsl:for-each select="$archSequence">
                <xsl:copy-of select="."/>
            </xsl:for-each>
        </sequence>
    </xsl:template>

</xsl:stylesheet>

You can use the tokenize function in a for expression to get a sequence of the separate values, then create an attribute node for each one. However, since XSLT doesn't let you create a bare attribute node with no element parent, you'll have to use a trick like this:

<xsl:variable name="archElements">
  <xsl:for-each select="for $attr in $initialNodeSet
                        return tokenize($attr, '[:;]')">
    <dummy arch="{.}" />
  </xsl:for-each>
</xsl:variable>

and then $archElements/dummy/@arch should be the set of separated arch attribute nodes that you require.

Complete example:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

  <xsl:output indent="yes" />

  <xsl:template match="/">
    <xsl:variable name="inputData">
      <a arch="value1;value2;value3" />
      <a arch="value1:value4" />
    </xsl:variable>

    <!-- create an example node set containing the two arch attribute nodes -->
    <xsl:variable name="initialNodeSet" select="$inputData/a/@arch" />

    <!-- tokenize and generate one arch attribute node for each value -->
    <xsl:variable name="archElements">
      <xsl:for-each select="for $attr in $initialNodeSet
                            return tokenize($attr, '[:;]')">
        <dummy arch="{.}" />
      </xsl:for-each>
    </xsl:variable>

    <!-- output to verify -->
    <r>
      <xsl:for-each select="$archElements/dummy/@arch">
        <c><xsl:copy-of select="."/></c>
      </xsl:for-each>
    </r>
  </xsl:template>
</xsl:stylesheet>

When run over any input document (the content is ignored) this produces

<?xml version="1.0" encoding="UTF-8"?>
<r>
   <c arch="value1"/>
   <c arch="value2"/>
   <c arch="value3"/>
   <c arch="value1"/>
   <c arch="value4"/>
</r>
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!