tags:

views:

21

answers:

2

I need to rearrange all the the child elements of an XML Document underneath the first Parent (and discard all other parent info)

In the example below, I need all 4 child elements under Parent[ParentField=1] and discard Parent[ParentField=X]

<xml>
    <Parent>
        <ParentField>1</ParentField>
        <Children>
            <Child>
                <id>1</id>
            </Child>
            <Child>
                <id>2</id>
            </Child>
        </Children>
    </Parent>
    <Parent>
        <ParentField>X</ParentField>
        <Children>
            <Child>
                <id>3</id>
            </Child>
            <Child>
                <id>4</id>
            </Child>
        </Children>
    </Parent>
</xml>

Resulting in XML like so:

<xml>
    <Parent>
        <ParentField>1</ParentField>
        <Children>
            <Child>
                <id>1</id>
            </Child>
            <Child>
                <id>2</id>
            </Child>
            <Child>
                <id>3</id>
            </Child>
            <Child>
                <id>4</id>
            </Child>
        </Children>
    </Parent>
</xml>
+2  A: 

This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:param name="pParentField" select="1"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Parent">
        <xsl:if test="ParentField = $pParentField">
            <xsl:call-template name="identity"/>
        </xsl:if>
    </xsl:template>
    <xsl:template match="Children">
        <xsl:copy>
            <xsl:apply-templates select="/xml/Parent/Children/Child"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Output:

<xml>
    <Parent>
        <ParentField>1</ParentField>
        <Children>
            <Child>
                <id>1</id>
            </Child>
            <Child>
                <id>2</id>
            </Child>
            <Child>
                <id>3</id>
            </Child>
            <Child>
                <id>4</id>
            </Child>
        </Children>
    </Parent>
</xml>
Alejandro
Thanks Alejandro - although I did eventually find a poor solution I will use yours!
nonnb
@nonnb: You are wellcome. Also, check my comment on your solution. Now it's no more downvoted...
Alejandro
A: 

FWIW the following also seems to work, but looks really 'orrible. Alejandro's solution is more elegant.

<xsl:template match="/">
    <xsl:apply-templates select="xml" />
</xsl:template>
<xsl:template match="xml">
    <Parent>
        <xsl:for-each select="Parent[1]">
            <ParentField><xsl select...><ParentField>
        </xsl:for-each>
        <Children>
        <xsl:for-each select="//Child">
            <Child>
                <...>
            </Child>
        </xsl:for-each>
    </Parent>
</xsl:template>
nonnb
@nonnb: Push style is not a bad solution when you know your schema. But I think it would be better: `<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:param name="pParentField" select="1"/><xsl:template match="/"><xml><Parent><xsl:copy-of select="Parent[ParentField=$pParentField]"/><Children><xsl:copy-of select="/xml/Parent/Children/Child"/></Children></Parent></xml></xsl:template></xsl:stylesheet>`
Alejandro
Full stylesheet in a comment!? That's for those complaining about XSLT verbosiness...
Alejandro