3

I'm having a hard time writing an XSLT to move all nodes having a same attribute value to the same level.

Here's an example:

<root>
    <object Name="1">
        <Property Name="a1" Value="a">
            <Property Name="a1.1" Value="a"/>
            <Property Name="a1.2" Value="a">
                <Property Name="a1.2.1" Value="a"/>
                <Property Name="a1.2.2" Value="a"/>
            </Property>
        </Property>
        <Property Name="b1" Value="b"/>
    </object>
</root>

Currently it is possible to nest properties with value a inside one another (there is no limit for the amount of nodes or nesting level). This model will change to only allow this type of property at the object level. Which should look like this after the transformation (the order of the elements doesn't matter):

<root>
    <object Name="1">
        <Property Name="a1" Value="a"/>
        <Property Name="a1.1" Value="a"/>
        <Property Name="a1.2" Value="a">
        <Property Name="a1.2.1" Value="a"/>
        <Property Name="a1.2.2" Value="a"/>
        <Property Name="b1" Value="b"/>
    </object>
</root>

I've tried solving this with this very similar question, the main difference being that in the example the node won't be copied, but its values will be used. I haven't been able to figure out how to copy the entire node though.

EDIT
The above example is over simplified. The properties will contain sub-elements which will have to be copied as well

<root>
    <object Name="1">
        <Property Name="a1" Value="a">
            <x>x1</x>
            <y>y1</y>
            <z>z1</z>
            <Property Name="a1.1" Value="a">
                <x>x1.1</x>
                <y>y1.1</y>
                <z>z1.1</z>
            </Property>
            <Property Name="a1.2" Value="a">
                <x>x1.2</x>
                <y>y1.2</y>
                <z>z1.2</z>
                <Property Name="a1.2.1" Value="a">
                    <x>x1.2.1</x>
                    <y>y1.2.1</y>
                    <z>z1.2.1</z>
                </Property>
                <Property Name="a1.2.2" Value="a">
                    <x>x1.2.1</x>
                    <y>y1.2.1</y>
                    <z>z1.2.1</z>
                </Property>
            </Property>
        </Property>
        <Property Name="b1" Value="b"/>
    </object>
</root>

Should become this after the transformation:

<root>
    <object Name="1">
        <Property Name="a1" Value="a">
            <x>x1</x>
            <y>y1</y>
            <z>z1</z>
        </Property>
        <Property Name="a1.1" Value="a">
            <x>x1.1</x>
            <y>y1.1</y>
            <z>z1.1</z>
        </Property>
        <Property Name="a1.2" Value="a">
            <x>x1.2</x>
            <y>y1.2</y>
            <z>z1.2</z>
        </Property>
        <Property Name="a1.2.1" Value="a">
            <x>x1.2.1</x>
            <y>y1.2.1</y>
            <z>z1.2.1</z>
        </Property>
        <Property Name="a1.2.2" Value="a">
            <x>x1.2.1</x>
            <y>y1.2.1</y>
            <z>z1.2.1</z>
        </Property>
        <Property Name="b1" Value="b"/>
    </object>
</root>

2 Answers 2

2

If you start with the XSLT identity template, all you need is just another template that matches Property that copies the element and attributes, but outputs the child elements after the element, rather that within it.

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

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" />
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

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

Note that I am assuming Property elements can only contain other Property elements as children here.

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

1 Comment

Thanks for your help. I had over simplified the example (I've edited my question). The nodes to copy also contain other child nodes than the properties which have to be copied along. With your XSLT, the property nodes lose their content.
1

Re your edited question - try it this way:

XSLT 1.0

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

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

<xsl:template match="Property">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()[not(self::Property)]"/>
    </xsl:copy>
    <xsl:apply-templates select="Property" />
</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.