Using XSLT 2.0 to parse the values of multiple att

2019-05-06 19:42发布

I'd like to be able to select all the attributes of a certain type in a document (for example, //@arch) and then take that node set and parse the values out into second node set. When I say "parse", in specific I mean I want to turn a node set like this:

arch="value1;value2;value3"
arch="value1:value4"

into a node set like this:

arch="value1"
arch="value2"
arch="value3"
arch="value1"
arch="value4"

or something like that; I want to get the individual values out of the attributes and into their own node.

If I can get it to that state, I've got plenty of methods for sorting and duplicate removal, after which I'd be using the finished node set for a publishing task.

I'm not so much looking for an tidy answer here as an approach. I know that XSLT cannot do dynamic arrays, but that's not the same as not being able to do something like dynamic arrays or something that mimics the important part of the functionality.

One thought that has occurred to me is that I could count the nodes in the first node set, and the number of delimiters, calculate the number of entries that the second node set would need and create it (somehow), and use the substring functions to parse out the first node set into the second node set.

There's usually a way working around XSLT's issues; has anyone worked their way around this one before?

Thanks for any help, Jeff.

标签: xslt xslt-2.0
2条回答
不美不萌又怎样
2楼-- · 2019-05-06 19:55

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>
查看更多
Fickle 薄情
3楼-- · 2019-05-06 20:15

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>
查看更多
登录 后发表回答