tags:

views:

40

answers:

2

Hello,

I have an XML document which I'm subjected to an XSLT. The structure is similar to:

<root>
  <item value="1">
     <object/>
  </item>
  <item value="2" />
     <object/>
  </item>
</root>

My goal is to end up with a transformed XML similar to:

<root>
 <parent>
  <object-one value-one="1"/>
 </parent>
 <parent>
  <object-two value-two="2"/>
 </parent>
</root>

My XSLT is similar to:

<xsl:apply-templates select="object" />


<xsl:template match="object">
    <xsl:call-template name="1" />
    <xsl:call-template name="2" />
</xsl:template>

<xsl:template name="1" match="object[item/@value = '1'">
  <xsl:element name="object-one" namespace="http://something.org"&gt;
    <xsl:attribute name="_Description">
      <xsl:value-of select="@_Type"/>
    </xsl:attribute>
    <xsl:attribute name="_Type">
      <xsl:value-of select="@_Amount"/>
   </xsl:attribute>
  </xsl:element>
</xsl:template>

 <xsl:template name="2" match="object[item/@value = '2'">
  <xsl:element name="object-two" namespace="http://something.org"&gt;
    <xsl:attribute name="OriginalAmount">
      <xsl:value-of select="@_Amount"/>
    </xsl:attribute>
  </xsl:element>
</xsl:template>

The problem is the all item nodes are having the same template applied. How can I apply a template to only specific nodes?

A: 

Perhaps you can make your matches more specific:

<xsl:template name="item1" match="item[@value=1]">
    <xsl:element name="item" namespace="http://something.org"&gt;
        <xsl:attribute name="_Description">
            <xsl:value-of select="@_Type"/>
        </xsl:attribute>
        <xsl:attribute name="_Type">
            <xsl:value-of select="@_Amount"/>
        </xsl:attribute>
    </xsl:element>
</xsl:template>

<xsl:template name="item2" match="item[@value=2]">
    <xsl:element name="item2_item" namespace="http://something.org"&gt;
        <xsl:attribute name="OriginalAmount">
            <xsl:value-of select="@_Amount"/>
        </xsl:attribute>
    </xsl:element>
</xsl:template>
Rob Fonseca-Ensor
Thank you for your suggestion, I've attempted to implement your suggestions but I still only get one template applied to all <object/> nodes.
alan
+1  A: 

EDIT: Now for different input sample (corrected for well-formed):

<root>
    <item value="1">
        <object/>
    </item>
    <item value="2" >
        <object/>
    </item>
</root>

This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:num="number" extension-element-prefixes="num">
    <num:num>one</num:num>
    <num:num>two</num:num>
    <xsl:template match="root">
        <xsl:copy>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="item">
        <parent>
            <xsl:apply-templates/>
        </parent>
    </xsl:template>
    <xsl:template match="object">
        <xsl:variable name="vTextNumber" select="document('')/*/num:*[number(current()/../@value)]"/>
        <xsl:element name="object-{$vTextNumber}">
            <xsl:attribute name="value-{$vTextNumber}">
                <xsl:value-of select="../@value"/>
            </xsl:attribute>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

Output:

<root>
    <parent>
        <object-one value-one="1" />
    </parent>
    <parent>
        <object-two value-two="2" />
    </parent>
</root>

EDIT 2: Now, what is wrong within your stylesheet fragment? Well, it looks like you don't know how the processor resolves template rules applying, also XPath navegation.

First, this object[item/@value = '1'] will match only this kind of input

<object>
    <item value="1"/>
</object>

Second, consider this three rules

1 -

<xsl:template match="object">
</xsl:template> 

2 -

<xsl:template name="1" match="object[../@value = '1']"> 
</xsl:template> 

3 -

<xsl:template name="2" match="object[../@value = '2']"> 
</xsl:template> 

With your last provided input, first object element (in document order) will match rules 1 and 2, and then the processor would resolve to apply rule 2. Why? From http://www.w3.org/TR/xslt#conflict

Next, all matching template rules that have lower priority than the matching template rule or rules with the highest priority are eliminated from consideration. The priority of a template rule is specified by the priority attribute on the template rule. The value of this must be a real number (positive or negative), matching the production Number with an optional leading minus sign (-). The default priority is computed as follows:

  • If the pattern contains multiple alternatives separated by |, then it
    is treated equivalently to a set of template rules, one for each
    alternative.
  • If the pattern has the form of a QName preceded by a
    ChildOrAttributeAxisSpecifier or has the form processing-instruction(Literal)
    preceded by a ChildOrAttributeAxisSpecifier, then the priority is 0.
  • If the pattern has the form NCName:* preceded by a
    ChildOrAttributeAxisSpecifier, then the priority is -0.25.
  • Otherwise, if the pattern consists of just a NodeTest preceded by a ChildOrAttributeAxisSpecifier, then the priority is -0.5.
  • Otherwise, the priority is 0.5.
Alejandro
I apologize for the shotty code snippets, I was in a rush to get that out there. I'm trying this suggestion now!
alan
I tried your suggestions but I was unable to get the desired results. It keeps applying only one of the two templates to every <object/>, whereas the goal is to apply both templates but only where the condition matches.Please see my revised question with more accurate code.
alan
The main problem I'm having is the value I'm testing for belongs to the parent of the node I'm applying the template to.
alan
@alan: `I tried your suggestions but I was unable to get the desired results` Well, **you've changed the input and desired output**! The best practice would be to ask another question. Otherwise you should add those news to your question, but not replacing. Take a look to my edit. If there is something not clear to you from my own stylesheet, just specificly ask.
Alejandro
I apologize. Again, last night I was in a rush and I fouled up the code. My edits reflect what I intended to ask last night. I appreciate your patience! I will try your new suggestions now.
alan
@Alejandro: I'm sorry if I was not clear, the element names are arbitrary, I am not trying to convert the values to part of the name. The problem is I need to test a value of the <item/> element; based on @value I need to apply one of two templates to the <object/> element, these two templates (though based on <object/>) will result in different element names.
alan
@alan: There is no general solutions. In declarative paradigm you must map an specific input to an specific output (or at least with well known schema). So, I can only explain to you what is wrong with your own template. See my edit.
Alejandro
Thanks! My problem was in how I was specifying the parent element in my match, namely: match="object[item/@value = '1']" whereas I needed to use match="object[../@value = '1']"Thank you!
alan