views:

217

answers:

2

I have XML in the following format which I want to reformat:

<blocks>
    <!-- === apples === -->
    <block name="block1">
     ...
    </block>
    <!-- === bananas === -->
    <block name="block2">
     ...
    </block>
    <!-- === oranges === -->
    <block name="block3">
     ...
    </block>
</blocks>

My problem is I can't figure out how to select the comments above each block tag. I have the following XSL:

<xsl:template match="//blocks">
        <xsl:apply-templates select="block" />
</xsl:template>
<xsl:template match="block">
    <xsl:apply-templates select="../comment()[following-sibling::block[@name = ./@name]]" />
    <xsl:value-of select="./@name" />
</xsl:template>
<xsl:template match="comment()[following-sibling::block]">
    <xsl:value-of select="."></xsl:value-of>
</xsl:template>

The output that I am trying for is:

=== apples ===
block1
=== bananas ===
block2
=== oranges ===
block3

But the best I can get is:

=== apples ===
=== bananas ===
=== oranges ===
block1
=== apples ===
=== bananas ===
=== oranges ===
block2
=== apples ===
=== bananas ===
=== oranges ===
block3

I am using PHP if that makes any difference.

A: 

You can apply the templates for comments also in your first apply-templates instead of the second one, so that it happens in order - Also, this solution is dependent on the order of the data in the source xml..

<xsl:template match="//blocks">
        <xsl:apply-templates select="block | comment()" />
</xsl:template>

PS:- You could avoid using "//" in your expressions as it can be non-optimal.

[EDIT] Complete Stylesheet

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:template match="//blocks">
  <xsl:apply-templates select="block | comment()"/>
 </xsl:template>
 <xsl:template match="block">
  <xsl:value-of select="./@name"/>
 </xsl:template>
 <xsl:template match="comment()">
  <xsl:value-of select="."/>
 </xsl:template>
</xsl:stylesheet>

Add the following statement if you want newlines, after you print the value in both the block and the comment.

<xsl:text>&#10;</xsl:text>
Thiyagaraj
Thanks, I prefer your version which seems simpler. Works great.
Rudolf Vavruch
+1  A: 

Your stylesheet is a bit overly complicated.

You should try the stylesheet below and you will find that it matches the output that you want!

<xsl:template match="//blocks">
        <xsl:apply-templates select="block" />
</xsl:template>
<xsl:template match="block">
    <xsl:apply-templates select="preceding-sibling::comment()[1]" />
    <xsl:value-of select="./@name" />
</xsl:template>
<xsl:template match="comment()">
    <xsl:value-of select="."></xsl:value-of>
</xsl:template>

This code always matches 1 or 0 comments that start right before the current block tag.

Peter Lindqvist
Thanks, this works. The reason for the complicated stylesheet is the actual XML I am using has comments in other places too, and I am attempting to avoid matching them as well.
Rudolf Vavruch