Variant XSLT 1.0 for individual values

I have an XSLT in which I create (from the input) intermediate variables, such as the following (hard code, but dynamic in nature):

<xsl:variable name="variableX"> <ValidCode CodePart="CP1" Code="C1"/> <ValidCode CodePart="CP2" Code="C2"/> <ValidCode CodePart="CP1" Code="C3"/> <ValidCode CodePart="CP2" Code="C4"/> <ValidCode CodePart="CP2" Code="C5"/> </xsl:variable> 

I want to iterate over individual occurrences of CodePart values. In XSLT 2.0, it's easy:

 <xsl:for-each select="distinct-values($variableX/ValidCode/@CodePart)">...</xsl:for-each> 

But what is the best way to do this in XSLT 1.0?
Please note: I cannot use the key, since it is a dynamically defined variable, and not part of the input file.

My input file contains a list of all possible parts of the code as follows:

 <root> <CodePart><value>CP1</value></CodePart> <CodePart><value>CP2</value></CodePart> <CodePart><value>CP3</value></CodePart> </root> 

So, I thought about //CodePart/value over //CodePart/value , providing uniqueness for starters. But then I need some kind of Xpath expression that includes the condition

"occurs in node -set of all values ​​of $ variableX / ValidCode / @ CodePart"

and use something like

 <xsl:for-each select="//CodePart[..condition..]/value">...</xsl:for-each> 

Is there a simple form of the Xpath expression I'm looking for?
Or is a different approach preferable?

+4
source share
5 answers

I don't think you can use XPath directly, but you should be able to check only valid values:

 <xsl:for-each select="//CodePart/value"> <xsl:variable name="CodePart" select="."/> <xsl:if test="$variableX/ValidCode[@CodePart=$CodePart]"> . . . </xsl:if> </xsl:for-each> 

or more simply (thanks to the comments):

 <xsl:for-each select="//CodePart/value"> <xsl:if test="$variableX/ValidCode[@CodePart=current()]"> . . . </xsl:if> </xsl:for-each> 

If $variableX not a node, but an XML fragment, you need to convert it to a node set - it depends on the implementation using Microsoft processors:

 <xsl:for-each select="//CodePart/value" xmlns:msxsl="urn:schemas-microsoft-com:xslt"> <xsl:if test="msxsl:node-set($variableX)/ValidCode[@CodePart=current()]"> . . . </xsl:if> </xsl:for-each> 
+2
source

Please note that I cannot use the key, since it is a dynamically defined variable and not part of the input file.

This is simply not true.

Here is a short, simple and effective solution using the keys (by the way, without using any conditional instructions or xsl:for-each at all :)):

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ext="http://exslt.org/common"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:key name="kCodeByPart" match="ValidCode" use="@CodePart"/> <xsl:variable name="variableX"> <ValidCode CodePart="CP1" Code="C1"/> <ValidCode CodePart="CP2" Code="C2"/> <ValidCode CodePart="CP1" Code="C3"/> <ValidCode CodePart="CP2" Code="C4"/> <ValidCode CodePart="CP2" Code="C5"/> </xsl:variable> <xsl:variable name="vCodes" select="ext:node-set($variableX)/*"/> <xsl:template match="/"> <xsl:apply-templates select= "$vCodes[generate-id() = generate-id(key('kCodeByPart', @CodePart)[1]) ]"/> </xsl:template> <xsl:template match="ValidCode"> Code Part: <xsl:value-of select="@CodePart"/> Codes: <xsl:apply-templates select= "key('kCodeByPart', @CodePart)/@Code"/> </xsl:template> <xsl:template match="@Code"> <xsl:value-of select="concat(., ' ')"/> </xsl:template> </xsl:stylesheet> 

When this conversion is applied to any XML document (not used), the desired, correct result is obtained :

  Code Part: CP1 Codes: C1 C3 Code Part: CP2 Codes: C2 C4 C5 

Explanation

As stated in the W3C XSLT 1.0 recommendation:

 "The key can be used to retrieve a key from a document other than the document containing the context node." 

In the provided solution, we don’t even use this feature - both the node context and the extracted key from one document - a temporary tree contained in $vCodes .

Again, it is indicated that the key can be used for several documents:

"A stylesheet declares a set of keys for each document using xsl: key element."

Simply put : indexing is performed in the current document (a document containing the context of the (current) node).

+4
source

One way to solve this problem (without a key) is to sort and test using preceding-sibling

 <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt"> <xsl:variable name="variableX"> <ValidCode CodePart="CP1" Code="C1"/> <ValidCode CodePart="CP2" Code="C2"/> <ValidCode CodePart="CP1" Code="C3"/> <ValidCode CodePart="CP2" Code="C4"/> <ValidCode CodePart="CP2" Code="C5"/> </xsl:variable> <xsl:variable name="refvarX" select="msxsl:node-set($variableX)" /> <xsl:template match="/"> <root> <xsl:for-each select="$refvarX/ValidCode"> <xsl:sort select="./@CodePart" data-type="text" /> <xsl:if test="not(./@CodePart = preceding-sibling::ValidCode/@CodePart)"> <cp> <xsl:value-of select="./@CodePart" /> </cp> </xsl:if> </xsl:for-each> </root> </xsl:template> </xsl:stylesheet> 
+2
source

In XPath 1.0, you can emulate the distinct-values function by looking for previous siblings with the same value.

I simplified your first document so that it can be easier to reproduce (even without using XSLT):

 <?xml version="1.0"?> <root> <ValidCode CodePart="CP1" Code="C1"/> <ValidCode CodePart="CP2" Code="C2"/> <ValidCode CodePart="CP1" Code="C3"/> <ValidCode CodePart="CP2" Code="C4"/> <ValidCode CodePart="CP2" Code="C5"/> <ValidCode CodePart="CP3" Code="C5"/> </root> 

Then, the following XPath expression will select only one CodePart attribute with each of the values ​​that appear in the document:

 //ValidCode/@CodePart[not(../preceding-sibling::ValidCode/@CodePart = .)] 

So, the condition for the CodePart attributes to select is that there is no previous brother to the <ValidCode> element whose CodePart attribute has the same meaning as the currently checked (selected) CodePart attribute.

+1
source

Using the node-set() function, for example, with a saxon:

 <xsl:template match="/"> <xsl:apply-templates select="root/CodePart/value[ . = saxon:node-set($variableX)/ValidCode/@CodePart]/> </xsl:template> <xsl:template match="value"> <!-- template for distinct value --> </xsl:template> 
0
source

All Articles