Jump to content

Transforming A "flat" Xml Into A Structure One

Recommended Posts

I am really new to XSLT; I started yesterday evening and transformed some very basic file. Now I am working on real-world example and having a problem. I am using a XML file produced by a famous desktop publishing application (Adobe InDesign) and its structure is something I am defining a "flat structure". Here is a example:

<?xml version="1.0" encoding="UTF-8"?><Root>    <service></service>    <description></description>    <description></description>    <description></description>    <service></service>    <description></description>    <description></description>    <service></service>    <description></description></Root>

While transforming it I have to iterate to within each item because the expected result should be:

<?xml version="1.0" encoding="UTF-8"?><Root>	<service>	   <description></description>	   <description></description>	   <description></description>	</service>	<service>		<description></description>		<description></description>	</service>	<service>		<description></description>	</service></Root>

Is there any way to reach this goal? Any help would be very appreciated --baz

Edited by thebaz
  • Like 1
Link to post
Share on other sites

With XSLT 2.0 (as supported by XSLT 2.0 processors like Saxon 9 or AltovaXML) you can simply do

 <xsl:output indent="yes"/> <xsl:template match="Root">  <xsl:copy>    <xsl:for-each-group select="*" group-starting-with="service">	  <service>	    <xsl:copy-of select="current-group() except ."/>	 </service>  </xsl:for-each-group> </xsl:copy></xsl:template>

With XSLT 1.0 you could process all service elements and key the other elements on their service sibling

<xsl:output indent="yes"/> <xsl:key name="k1" match="Root/*[not(self::service)]" use="generate-id(preceding-sibling::service[1])"/> <xsl:template match="Root">  <xsl:copy>	 <xsl:apply-templates select="service"/>  </xsl:copy></xsl:template> <xsl:template match="service">  <xsl:copy>    <xsl:copy-of select="key('k1', generate-id())"/>  </xsl:copy></xsl:template>

Untested code typed directly here in the forum editor but it should give you an idea.

  • Like 1
Link to post
Share on other sites

The stylesheet makes a shallow copy of the "Root" element and then processes all "service" child elements of the "Root" element. The template for the "service" elements again makes a shallow copy of each service element and populates it by making a deep copy of the result of calling key('k1', generate-id()). With the "k1" key definition being

<xsl:key name="k1" match="Root/*[not(self::service)]" use="generate-id(preceding-sibling::service[1])"/>

we have instructed the XSLT processor to index all child elements of the Root element (Root/*) which are not "service" elements ([not(self::service)]) with the generated id of the first preceding sibling service element. With that definition of the key "k1" the call "key('k1', generate-id())" where the context node is a "service" elements returns a node-set of all element that have that "service" element as the first preceding sibling service element. So that way we can wrap all those elements into a service element in the transformation result.That is a rough description but if you are not familiar with XSLT specifics like keys there are lots of tutorials and books you can consult to get a better understanding.

  • Like 1
Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...