tags:

views:

249

answers:

2

This question actually asks something quite different. See the comments to @Tomalak's answer to understand what the OP really wanted. :(

Is there a way to store a variable/param during a for-each loop in a sort of array, and use it in another template, namely <xsl:template match="Foundation.Core.Classifier.feature">. All the classname values that appear during the for-each should be stored. How would you implement that in XSLT? Here's my current code.

<xsl:for-each select="Foundation.Core.Class">       
 <xsl:for-each select="Foundation.Core.ModelElement.name">
  <xsl:param name="classname">
   <xsl:value-of select="Foundation.Core.ModelElement.name"/>
  </xsl:param>
 </xsl:for-each>
 <xsl:apply-templates select="Foundation.Core.Classifier.feature" /> 
</xsl:for-each>

Here's the template in which the classname parameters should be used.

<xsl:template match="Foundation.Core.Classifier.feature">
 <xsl:for-each select="Foundation.Core.Attribute">
  <owl:DatatypeProperty rdf:ID="{Foundation.Core.ModelElement.name}">
   <rdfs:domain rdf:resource="$classname" />
  </owl:DatatypeProperty>
 </xsl:for-each>
</xsl:template>

The input file can be found at http://krisvandenbergh.be/uml_pricing.xml

+3  A: 

No, it is not possible to store a variable in a for-each loop and use it later.

This is because variables are write-once in XSLT (once set they are immutable) and they are strictly scoped within their parent element. Once processing leaves the for-each loop, the variable is gone.

XSLT does not work as an imperative programming language, but that's what you seem to be trying here. You don't need <xsl:for-each> in 98% of all cases and should not use it because it clogs your view of how XSLT works. To improve your XSLT code, get rid of all <xsl:for-each> loops you have (all of them, I mean it) and use templates instead:

<xsl:template match="Foundation.Core.Class">
  <xsl:apply-templates select="
    Foundation.Core.Classifier.feature/Foundation.Core.Attribute
  " />
</xsl:template>

<xsl:template match="Foundation.Core.Attribute">
  <owl:DatatypeProperty rdf:ID="{Foundation.Core.ModelElement.name}">
    <rdfs:domain rdf:resource="{
      ancestor::Foundation.Core.Class[1]/Foundation.Core.ModelElement.name[1]
    }" />
  </owl:DatatypeProperty>
</xsl:template>

(I'm not sure if the above is what you actually want, your question is rather ambiguous.)

Note the use of the XPath ancestor axis to refer to an element higher in the hierarchy (you seem to want the <Foundation.Core.ModelElement.name> of the parent class).

PS: Your XML is incredibly bloated and strongly redundant due to structured element names. Structure should come from... well... structure, not from elements like <Foundation.Core.Classifier.feature>. I'm not sure if you can do anything about it, though.


Addition:

To solve your xmi.id / xmi.idref problem, the best way is to use an XSL key:

<!-- this indexes all elements by their @xmi.id attribute -->
<xsl:key name="kElementByIdref" match="*[@xmi.id]" use="@xmi.id" />

<!-- now you can do this -->
<xsl:template match="Foundation.Core.DataType">
  <dataTypeName>
   <!-- pull out the corresponding element from the key, output its value -->
   <xsl:value-of select="key('kElementByIdref', @xmi.idref)" />
  </dataTypeName>
</xsl:template>

To better understand how keys work internally, you can read this answer I gave earlier. Don't bother too much with the question, just read the lower part of my answer, I explained keys in terms of JavaScript.

Tomalak
Thank you so much for your answer. One question though. Please consider the following match. <xsl:template match="some_element"> <xsl:variable name="{@id}">VALUE</xsl:variable></xsl:template>How do I refer to this variable in another template? Please look at the code below. @xmi.idref should be replaced by the variable (VALUE) according to the value in @xmi.idref, since @xmi.idref and @id are the same. Similar like eval() behaviour in PHP, how would that be done? <rdfs:range rdf:resource="{Foundation.Core.StructuralFeature.type/Foundation.Core.DataType/@xmi.idref}" />
Kris Van den Bergh
@krisvandenbergh: Read my answer again: You can't refer to a variable from another template, because it's out of scope there. It would be helpful if you reduced your XML to the relevant part and showed the expected output in your question.
Tomalak
(+1) - good answer. However, the advice to replace *every* `<xsl:for-each>` is not 100% correct. There are cases in XSLT 1.0 where `<xsl:for-each>` is necessary.
Dimitre Novatchev
I think the real intention of the question was how to implement multi-step processing -- this is what I described in my answer.
Dimitre Novatchev
@Dimitre: That's why I spoke of 98%. ;) My point is this - only when you know how to write a stylesheet entirely without using for-each will you be able to recognize the 2% where you benefit from it. Whereas when you are using for-each casually most of the time you shoot yourself in the foot and don't even know it.
Tomalak
"Shoot in the foot" is also a little bit exagerated. I'd say, you create less reusable, less powerful code.However, if the only purpose is to demonstrate something quickly and the space is at premium (such as in a presentation slide), `<xsl:for-each>` may come handy. This aside, I of course completely agree and recommend trying to use `<xsl:apply-templates>` as much as possible.
Dimitre Novatchev
@Dimitre: Exaggeration is used as a rhetorical device here, I just tried to make a point. :)
Tomalak
@Tomalak: Why not try to say it exactly, and not use absolute statements that are (almost never) completely true?
Dimitre Novatchev
@Dimitre: I did not make an absolute statement, I said "98% of all cases". Apart from that, from my experience beginners respond better to exaggeration. If you say `<xsl:for-each>` is "sub-optimal, and there are better alternatives", they continue to use it everywhere because "sub-optimal" is good enough for most beginners. If you say "it's bad, avoid it at all costs" chances of getting through to them are better, IMHO. *PS: I don't think the question did benefit from your edit: Most visitors would see your irritated comment but not why you made it. Always edit with the common googler in mind*
Tomalak
"common googler"? What is this? :) As for my edit, it did almost the impossible: made this question at least partly useful to people, who want really to learn something.
Dimitre Novatchev
A: 

Ok, I now understand why for-each is not always needed. Consider the code below.

<Foundation.Core.DataType xmi.id="UID71848B1D-2741-447E-BD3F-BD606B7FD29E">
<Foundation.Core.ModelElement.name>int</Foundation.Core.ModelElement.name>
</Foundation.Core.DataType>

It has an id UID71848B1D-2741-447E-BD3F-BD606B7FD29E

Way elsewhere I have the following.

    <Foundation.Core.StructuralFeature.type>
<Foundation.Core.DataType xmi.idref="UID71848B1D-2741-447E-BD3F-BD606B7FD29E"/>
</Foundation.Core.StructuralFeature.type>

As you can see, both codes have the same ID. Now I want to output "int", everytime this ID appears somewhere in the document. So basically idref="UID71848B1D-2741-447E-BD3F-BD606B7FD29" should be replaced by int, which can be easily derived from the Foundation.Core.ModelElement.name element.

Above is the main reason why I would like to store it in a variable. I don't get it how this can be dealt with using XSLT. If someone could elaborate on this, I hope there exists some kind of pattern to solve such a problem, since I need it quite often. What would be a good approach?

I understand this is maybe a bit off-topic, but I am willing to ask it in this thread anyway since this problem is very close to it.

Kris Van den Bergh
The key to solving this problem are XSL keys (pun intended). See my answer.
Tomalak
This is a very different question from your original one. Please, ask it as a new question. Also, please, provide a minimal example (but complete XML document), with simple, natural element names. Provide the result that must be produced. Learn how to ask a question, if you really want somebody to decide to spend their time answering it.
Dimitre Novatchev
If you look-up the Internet for XSLT look-up (pun intended), you'll find good solutions.
Dimitre Novatchev