views:

179

answers:

4

I'm kind of new to using XSL. I'm trying to convert an XML file into another XML file with a different structure using XSL. The input section of the XML goes like this:

<tag>
    <Keyword>Event: Some Text</Keyword>
    <Keyword>Group: Some Text</Keyword>
    <Keyword>Other: Some Text</Keyword>
</tag>

I would like the desired output to be:

<tag>
    <event> Some Text </event>
    <group> Some Text </group>
    <other> Some Text </other>
</tag>

My current XSL file:

<xsl:for-each select="tag">
    <xsl:if test="starts-with(Keyword, 'Event: ')">
        <event>
          <xsl:value-of select="substring-after(Keyword, 'Event: ')"/>
        </event>
    </xsl:if>
    <xsl:if test="starts-with(Keyword, 'Group: ')">
        <group>
          <xsl:value-of select="substring-after(Keyword, 'Group: ')"/>
        </group>
    </xsl:if>
    <xsl:if test="starts-with(Keyword, 'Other: ')">
        <other>
          <xsl:value-of select="substring-after(Keyword, 'Other: ')"/>
        </other>
    </xsl:if>
</xsl:for-each>

The current output only shows the event node and does not display the remaining nodes:

<tag>
    <event> Some Text </event>
</tag>

I tried switching the 'group' section with the 'event' section in the XSL, however all the child nodes are not displayed probably due to the ordering of the keyword nodes in the input XML. So how can I read all the keyword nodes and convert them to the respective new nodes for display?

+2  A: 
Mads Hansen
thank you so much... it works!
Hariz
You should look at @Pierre's solution and consider trying to do more template matches than for-each statements in your XSLT. It is kind of a different way of doing things(declarative vs. procedural) that is sometimes hard to grasp at first, but it will make development and maintenance a lot easier.
Mads Hansen
+2  A: 

Here is my solution. You don't have to create all those if statements by using xsl:element

<xsl:template match="tag">
<xsl:apply-templates select="Keyword"/>
</xsl:template>

<xsl:template match="Keyword">
 <xsl:variable name="tag" select="normalize-space(substring-before(.,':'))"/>
 <xsl:value-of select="concat('&lt;',$tag,'&gt;')" disable-output-escaping="yes"/>
 <xsl:value-of select="normalize-space(substring-after(.,':'))"/>
 <xsl:value-of select="concat('&lt;/',$tag,'&gt;')" disable-output-escaping="yes"/>
</xsl:template>
Pierre
+1 Much more clean. I was going to add something like that to my answer, but you beat me to it!
Mads Hansen
hmm it says the character '(' cannot be included in the element name..
Hariz
Ah... You may be right. I changed xsl:element to xsl:value-of with @disable-output-escaping='yes'.
Pierre
Why you didnt use xsl:element and xsl:attribute for dynamic construction of elements on runtime ? Its much better for element construction, but, probably, in this case simple concatenation will work either
Alexey Shcherbak
Alexey, here we need to generate the name of the element, not one of its attribute.
Pierre
+1 for getting rid of the for-each
Filburt
I liked the original code better. All you needed to do was wrap the expression in `{}`, so that the expression was evaluated as an Attribute Value Template. Disable output escaping in order to jam angle brackets in the output is an evil hack that should not be used.
Mads Hansen
@Pierre: answered as post due to char limit and lack of syntax highlighting in comments
Alexey Shcherbak
+3  A: 

@Pierre: I understand initial question and your solution correctly ;) Your way for XML elements construction IMHO bit of hackly. XSLT have special set for dynamic construction of xml elements. So your template match="Keyword" can look like this

<xsl:template match="Keyword">
  <xsl:variable name="tag" select="normalize-space(substring-before(.,':'))"/>
  <xsl:element name="{$tag}">
    <xsl:value-of select="normalize-space(substring-after(.,':'))"/>
  </xsl:element>
</xsl:template>

So you have option to easily add attributes to output XML elements if you need, and won't bother with disable-output-escaping etc.

Alexey Shcherbak
A: 

Thanks! Now I'd like to convert the XML file into another XML file since the input section of the XML goes like this:

<set>
    <object> Value </object>
        <name> Value </name>
        <tag>
            <event> Value </event>
            <group> Value </group>
            <other> Value </other>
        </tag>
    <object>
    <object>...</object>
</set>

And the output to be:

<set>
    <event>
        <group>
            <object name="value">
                <tag>
                    <other> Value </other>
                </tag>
            </object>
        <group>
        <group>...</group>
    <event>...</event>
</set>

Meaning to say how do I search through the input xml and group the objects according to say the 'event' nodes followed by the 'group' nodes with the same value instead?

Hariz
Don't use the answer box to ask a question. Either edit your original question or ask a new question.
benzado