views:

52

answers:

2

Hi

I'm stuck trying to work within these constraints, I'm using XSLT 1.0 {under .net}. I'd like to be able to do the following: i'm xsl:for-each'ing through a set of nodes of type

   <node>
     <data> unknown unstructured xml </data>
     <owner></owner>
   </node>

i'd like to be able to output

   <node>
     <data> unknown unstructured xml </data>
     <!--RESULT of calling an XSL template with certain parameters -->
   </node>

from my search so far i thought i could do something like in here:

    <xsl:copy> 
        <xsl:apply-template name="findownerdetails">
           <xsl:with-param name="data" select="something" />
        </xsl:apply-template> 
    </xsl:copy> 

but this is apparently not valid. any suggestions how to get this working or to acheive something similar? I'm afraid i cant just call apply-templates as the template i want will depend on some data i am building up as i for-each through a list of node elements.

Any advice appreciated

+1  A: 
<xsl:template match="node">
  <node>
    <xsl:copy-of select="data"/>
    <!-- assuming this next bit in your question example
    is something you are happy with -->
    <xsl:call-template name="findownerdetails">
      <xsl:with-param name="data" select="something" />
    </xsl:call-template> 
  </node>
</xsl:template>
Jon Hanna
Thanks, I wish i'd thought of doing that a while back. For reference: What would happen if node element had a number of unknown attirbutes i needed to copy? Also is it possible to do a <xsl:copy-of and select every node except the owner node?
GreyCloud
You could do <xsl:copy-of select="*[local-name() != 'owner']"/> which answers both your questions. With more complicated cases, or the possibility of an "owner" element from a different namespace then identity-with-override (as in Dimitre's answer) becomes more elegant (we can expand by Dimitre's and my answer to deal with more complicated cases; mine is more concise below a certain level of complexity and Dimitre's above it, which is also what makes Dimitre's more general and one every XSLT coder should know).(p.s., if the above helped, accepting the answer would be nice).
Jon Hanna
thanks for the extra info Jon, that looks like it will do the trick. Since Dimitre's answer doesnt handle passing parameters and yours does, i will accept your answer - thanks :)
GreyCloud
@GreyCloud: Your statement that "Dimitre's answer doesnt handle passing parameters" is not true -- you simply haven't requested this and your problem doesn't need passing parameters. I have updated my answer explaining these points. This is a good moment for you to learn something fundamental and very helpful.
Dimitre Novatchev
@Dimitre - from question "I'm afraid i cant just call apply-templates as the template i want will depend on some data i am building up as i for-each through a list of node elements. ", see comment below
GreyCloud
@@GreyCloud: The quoted sentence is not even grammatically correct English -- why do you think anyone would understand it?
Dimitre Novatchev
@Jon Hanna: Your answer is wrong. There is no `@name` in `apply-templates` instruction. It's `call-template` instruction wich has `@name`.
Alejandro
@Alejandro right you are. I copied the bit in the original question that the querant seemed happy with, without checking it, and it does indeed have an incorrect apply-templates. I'm changing to call-template assuming that was what was meant (rather than apply-templates being right, and some select - perhaps with a mode - being what should be indicating the matching criteria).
Jon Hanna
@GreyCloud, you certainly should be able to adapt an apply-templates to reference variables, pass parameters, or whatever other way you want to pass the state in question.
Jon Hanna
GreyCloud
+2  A: 

This is a classic example of a problem that is best solved using and overriding the identity rule :

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="owner">
    <owner-details>
      <xsl:value-of select="."/>
    </owner-details>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on this XML document (based on the provided XML document with added attributes and owner details):

<node attr1="x" attr2="y">
    <data> unknown unstructured xml </data>
    <owner>
        <details>
            <name>John Smith </name>
            <profession>XSLT programmer</profession>
        </details>
    </owner>
</node>

The wanted result is produced:

<node attr1="x" attr2="y">
   <data> unknown unstructured xml </data>
   <owner-details>John Smith XSLT programmer</owner-details>
</node>

Do note:

  1. The identity template copies every node in the document "as-is" in a recursive fashion.

  2. We only override the identity template for elements that we want to be processed in another way. Any template, whose match pattern is more specific than that of the identity template, overrides it -- the XSLT processor always chooses the most specific matching template for a node.

  3. Using and overriding the identity rule is the most fundamental, the most powerful, the most general and most elegant XSLT design pattern. It is used exclusively in almost all XSLT transformations: for deleting/renaming/modifying/adding specific nodes and keeping all other nodes intact.

  4. The OP has suggested in a comment that this solution doesn't allow parameters to be passed. This is not true. Any template (including the identity rule) can be written to have parameters -- when this is needed. In this particular case it is not necessary to pass parameters via templates.

  5. The template matching owner doesn't need to call another template -- all the owner - specific processing can be done here.

Dimitre Novatchev
Hi Dimitre, thank you for your solution, i saw a similar answer in another thread but am afraid it will not work since i cannot pass any parameters through the <xsl:apply-templates select="node()|@*"/> call.
GreyCloud
@GreyCloud: You don't understand: 1. You *can* pass parameters through any template. For the problem in your question this is not needed. Modify the question so that it is required to pass parameter(s) and I'll show you how. 2. In the template that matches `owner` you don't need to call another template and pass paramrter(s) to it -- you can just process the `owner` element (and its subtree) within this template -- and this is what my answer suggests.
Dimitre Novatchev
how can i pass parameters through " <xsl:apply-templates select="node()|@*"/> " ?
GreyCloud
@GreyCloud: `<xsl:apply-templates select="node()|@*"><xsl:with-param name="paramName" select="whateverNecessary"/></xsl:apply-templates>`
Dimitre Novatchev
@Dimitre: +1 for full and correct answer. Also it looks like you have not been listen when ever you'd answered this simple question (I followed the OP link)
Alejandro
thanks Dimitre, your comment is the missing peice that enables me to get your solution working in addition to Jon Hanna's. however Jon's solved the problem i was facing at the time, so i have accepted it, i have just upvoted your answer - i hope this is correct ettiquette at stackoverflow, i am still new here.
GreyCloud
@GreyCloud: Thanks, that's not a problem. Next time simply ask: "How can I pass a parameter with `<xsl:apply-templates>`". I wouldn't guess that there was someone that doesn't know how to do this. :)
Dimitre Novatchev