Hobbes
Hobbes

Reputation: 2115

XSLT: do a comparison based on a list

I've got a list, with paired values:
aircon, aircon_no - these two values are a pair
ABS, ABS_no
heatedseats, heatedseats_no
etc.

So a pair is two values that start with the same substring. These values are stored in the @type attribute. I've got about 50 pairs in my data.

I want to process an XML which looks like this:

<object type="aircon" id="1">
   ..
</object>
<object type="ABS" id="2">
   ..
</object>
<object type="heatedseats" id="3">
   ..
</object>
<object type="ABS" id="4">
   ..
</object>
<object type="ABS_no" id="5">
   ..
</object>

This is a short excerpt, the data contains hundreds of "object" nodes.

The operation depends on the type attribute of the "object" node, and the type attr. of the preceding "object" node. I could find those like this:

<if test="@type='ABS_no' and preceding-sibling::object/@type='ABS'"> ...

My list of paired values is quite long, so I get a long list of these if statements.

Is there a way to avoid creating a long list of if statements, and instead have a more general statement that works for each of my pairs:

<if test="@type='-second value of a pair-' and preceding-sibling::object[1]/@type='-first value of that same pair-'">

and get the whole list of pairs into a single statement?

The goal is to find node ABS_no if it is preceded by node ABS (so @id='5' in my example list), and do this for each pair of values.

The XSLT is passthrough, except for one template that matches the nodes. If these pass the test, an extra subnode is added to the node.

I use XSLT2, but XSLT3 is an option if it helps.

Upvotes: 0

Views: 68

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 116993

a pair is two values that start with the same letter.
...
The goal is to find node B2 if it is preceded by node B1

I am not sure that constitutes a well-defined problem (what if there is a sequence B1, B2, B3?), but I believe this will do what you asked for:

<xsl:for-each select="object[position() > 1 and starts-with(@type, substring(preceding-sibling::object[1]/@type, 1, 1))]">
    <!-- do something -->
</xsl:for-each>

--- added in responses to your edit ---

the _ is the separator between the common substring of a pair and the unique part of the values

In such case you could select (or match):

object[substring-before(@type, '_') = preceding-sibling::object[1]/@type]

But if you can use XSLT 2.0, grouping might be a better alternative.

Upvotes: 1

Michael Kay
Michael Kay

Reputation: 163322

You could use <xsl:for-each-group select="object" group-by="replace(@type, '_.*$', '')" and then each pair would be a group.

Upvotes: 1

Related Questions