Jump to content

Transforming A "flat" Xml Into A Structure One


thebaz

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

Link to comment
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.

Link to comment
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.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...