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

≡放荡痞女 提交于 2019-12-22 08:15:37

问题


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.


回答1:


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>



回答2:


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>


来源:https://stackoverflow.com/questions/18339114/using-xslt-2-0-to-parse-the-values-of-multiple-attributes-into-an-array-like-str

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!