1

I have a xml file like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <i xmlns="http://stuff.com" >
        <v>
            <iNumber>0118</iNumber>
            <s>1</s>
            <a>24</a>
            <p>2175</p>
            <p>2175</p>
            <p>4534</p>
        </v>
    </i>

But I need the export to look like this.

    <?xml version="1.0" encoding="UTF-8"?>
    <i xmlns="http://stuff.com" >
        <v>
            <iNumber>0118</iNumber>
            <s>1</s>
            <a>24</a>
            <p>2175</p>
        </v>
        <v>
            <iNumber>0118</iNumber>
            <s>1</s>
            <a>24</a>
            <p>2175</p>
        </v>
            <iNumber>0118</iNumber>
            <s>1</s>
            <a>24</a>
            <p>4534</p>
        </v>
    </i>

I am new to xslt so I would like some help with transforming this xml please

Here's my attempt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
                xmlns:stuff="http://stuff.com" exclude-result-prefixes="stuff">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*"/>

 <xsl:template match="stuff:v">
    <xsl:copy>
        <xsl:copy-of select="ancestor::stuff:v/stuff:p"/>
    <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

 </xsl:stylesheet>

I've updated my question to show my attempt

4
  • 2
    Do you have a specific question or difficulty? Or are you just looking for someone to do your work for you? Commented Feb 11, 2017 at 2:04
  • I would like some some help writing it Commented Feb 11, 2017 at 3:25
  • 1
    Start writing, we''ll help you when you get stuck. Commented Feb 11, 2017 at 10:56
  • I updated my question with my attempt. Unfortunately it did not work. Commented Feb 13, 2017 at 15:29

2 Answers 2

1

This should do the trick:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:stuff="http://stuff.com" exclude-result-prefixes="stuff">

    <xsl:output version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" />

    <xsl:strip-space elements="*"/>

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

    <xsl:template match="stuff:v">
        <xsl:for-each select="stuff:p">
            <xsl:apply-templates select="ancestor::stuff:v" mode="copying">
                <xsl:with-param name="pId" select="generate-id()" /> 
            </xsl:apply-templates>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="stuff:v" mode="copying">
        <xsl:param name="pId" />
        <xsl:copy>
            <xsl:apply-templates select="@*|node()">
                <xsl:with-param name="pId" select="$pId" /> 
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="stuff:p">
        <xsl:param name="pId"/>
        <xsl:if test="generate-id() = $pId">
            <xsl:copy-of select="." />
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

Some explanation. The first template matching node()|@* just copies things by default.

The second template matching stuff:v will take over when a stuff:v element is encountered. It will iterate over all stuff:p elements in it, and for each of them applies matching templates to its stuff:v ancestor. However, it enters a mode I named "copying" and calls the templates with a parameter pId which is the id of the p element. Each element in XML has some implicit id that uniquely identifies it.

This way we end up in the next template, which also matches stuff:v but only in mode "copying". It accepts a pId parameter, copies the v element and then applies templates to all its attributes and child nodes, no longer with the "copying" mode but with the pId parameter. That last part is crucial.

The final template matches stuff:p but will only copy it if the id of that element is the same as the one given in the pId parameter.

So basically, we get to stuff:v, go through each stuff:p in it, for each of them it goes to stuff:v again in a different mode to match another template (otherwise it would recurse infinitely), and from there copy everything but for stuff:p elements require that they match a parameter passed into the template.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you so much for the anser @G_H it works great but the only thing is that it does not copy the stuff:iNumber. It copies everything else, stuff:s, stuff:a, stuff:p
@1122335 That's odd, it copies the iNumber element just fine in my test. Are there multiple namespaces in the XML you're using for testing? Which XSLT processor is this?
Nevermind! I figured it out, thank you so much for the answer!
1

Why don't you do simply:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:stuff="http://stuff.com" 
exclude-result-prefixes="stuff">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/stuff:i">
    <i xmlns="http://stuff.com">
        <xsl:for-each select="stuff:v/stuff:p">
            <v>
                <xsl:copy-of select="../*[not(self::stuff:p)]"/>
                <xsl:copy-of select="."/>
            </v>
        </xsl:for-each>
    </i>
</xsl:template>

</xsl:stylesheet>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.