Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old August 18th, 2009, 05:23 PM
Registered User
 
Join Date: Aug 2009
Posts: 5
Thanks: 2
Thanked 0 Times in 0 Posts
Default XML Restructuring using XSLT

Hi,

I need some help. I have an XML file and I need to change the structure of the information. I've included an example
Code:
<model>
	<concept name="example" uniqueID="10"/>
	<node>
		<name>StratA</name>
		<uniqueID>11</uniqueID>
		<class>static</class>
		<category>user</category>
		<parent>example</parent>
		<parentID>10</parentID>
		<functionality available="false"/>
	</node>	
	<node>
		<name>TempA</name>
		<uniqueID>14</uniqueID>
		<class>static</class>
		<category>user</category>
		<parent>example</parent>
		<parentID>10</parentID>
		<funtionality available="true"/>
	</node>
	<function nodename="TempA">
		<loop>
			<name>loop1</name>
		</loop>
		<fork>
			<kind>OR</kind>
		</fork>
		<sub>
			<type>dynamic</type>
		</sub>
	</function>
</model>
The above is what my XML looks like at the minute
and below is what I'd like it to look like.

Code:
<model>
	<concept name="example" uniqueID="10"/>
	<node>
		<name>StratA</name>
		<uniqueID>11</uniqueID>
		<class>static</class>
		<category>user</category>
		<parent>example</parent>
		<parentID>10</parentID>
		<functionality available="false"/>
	</node>	
	<node>
		<name>TempA</name>
		<uniqueID>14</uniqueID>
		<class>static</class>
		<category>user</category>
		<parent>example</parent>
		<parentID>10</parentID>
		<funtionality available="true">
			<loop>
				<name>loop1</name>
			</loop>
			<fork>
				<kind>OR</kind>
			</fork>
			<sub>
				<type>dynamic</type>
			</sub>
		</funtionality>
	</node>
</model>
I'm new to XML and XSLT and I need the XML to be restructured so that the functionality tags occur within the node that it corresponds to. I'm guessing if you know what you are doing a couple of lines would do it but I'm totally clueless. Any help would be appreciated.

Thanks
sillypepper
 
Old August 18th, 2009, 06:25 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

I'm guessing a bit at the requirement. Assuming that a <node> is followed by zero or one <function> elements, and that is the one that it "corresponds to", then you want an identity template:

Code:
<xsl:template match="*">
  <xsl:copy>
     <xsl:copy-of select="@*"/>
     <xsl:apply-templates/>
  </xsl:copy>
</xsl:template>
and then overriding templates where special action is needed:

Code:
<xsl:template match="functionality">
  <xsl:copy>
     <xsl:copy-of select="@*"/>
     <xsl:apply-templates/>
     <xsl:copy-of select="../following-sibling::*[1][self::function]/*"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="function"/>
Read that path expression as "select the first following sibling element of the parent of this node, provided it is a function element, and then copy its children".
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
The Following User Says Thank You to mhkay For This Useful Post:
sillypepper (August 18th, 2009)
 
Old August 18th, 2009, 07:01 PM
Registered User
 
Join Date: Aug 2009
Posts: 5
Thanks: 2
Thanked 0 Times in 0 Posts
Default

Thanks for the quick reply.

Actually the file will have all nodes followed by function elements. See below for an idea (I've left the details out as they are in the original post).
Code:
<node></node>
<node></node>
<node></node>
<node></node>
<node></node>
<function></function>
<function></function>
There is no order to the nodes or function elements. Each node can have at most one function element associated with it or none at all. Also each function element will be associated with only one node. The nodename attribute in the function element identifies what node it should be restructured into. All nodenames will be unique.

I need to restructure using nodename to determine the place of the functionality. The functionality element attribute is set to true if a function element is associated with it.

I'm sorry for the stupid questions. I'm totally new to xml xslt and I just need it for this one thing.

Thanks
 
Old August 19th, 2009, 06:23 AM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

Note that your posted XML sample seems to have typos in element names like 'funtionality'. So I first corrected the XML input to have consistent spelling:
Code:
<model>
    <concept name="example" uniqueID="10"/>
    <node>
        <name>StratA</name>
        <uniqueID>11</uniqueID>
        <class>static</class>
        <category>user</category>
        <parent>example</parent>
        <parentID>10</parentID>
        <functionality available="false"/>
    </node>    
    <node>
        <name>TempA</name>
        <uniqueID>14</uniqueID>
        <class>static</class>
        <category>user</category>
        <parent>example</parent>
        <parentID>10</parentID>
        <functionality available="true"/>
    </node>
    <function nodename="TempA">
        <loop>
            <name>loop1</name>
        </loop>
        <fork>
            <kind>OR</kind>
        </fork>
        <sub>
            <type>dynamic</type>
        </sub>
    </function>
</model>
Then this stylesheet
Code:
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">
  
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  
  <xsl:key name="k1" match="function" use="@nodename"/>
  
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="functionality[@available = 'true']">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates select="key('k1', parent::node/name)/node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="function"/>

</xsl:stylesheet>
should do what you want.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
The Following User Says Thank You to Martin Honnen For This Useful Post:
sillypepper (August 19th, 2009)
 
Old August 19th, 2009, 06:50 AM
Registered User
 
Join Date: Aug 2009
Posts: 5
Thanks: 2
Thanked 0 Times in 0 Posts
Default

Thanks. That works a treat. I'm sorry about the typos I was creating a simple example of what I wanted to happen to save posting my huge xml. I've tweaked it for my real XML and it's all good.

Thanks so much.
 
Old August 19th, 2009, 06:55 PM
Registered User
 
Join Date: Aug 2009
Posts: 5
Thanks: 2
Thanked 0 Times in 0 Posts
Default

Hopefully, my last silly question.

If I wanted to move

Code:
<function nodename="TempA">
		<loop>
			<name>loop1</name>
		</loop>
		<fork>
			<kind>OR</kind>
		</fork>
		<sub>
			<type>dynamic</type>
		</sub>
</function>
into the correct <node> but not into functionality, what do I need to change to the stylesheet above?

I've tried altering it and ended up with very mixed results but nothing like what I want. I don't know what to change.

Any ideas appreciated.

Thanks
 
Old August 19th, 2009, 07:08 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

When your code doesn't work, it's always a good idea to post it, because then we can see what concept you haven't grasped and try to explain it to you. Without that, there's a danger we just post more code which you still don't understand, and that helps no-one.

The basic design of this stylesheet is that it has a default template rule which copies an element and processes its children, and then it has special template rules for elements that need to be handled differently. The design change you are proposing is that it's the "node" element that needs to be handled differently, not the "functionality" node.
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
 
Old August 19th, 2009, 07:54 PM
Registered User
 
Join Date: Aug 2009
Posts: 5
Thanks: 2
Thanked 0 Times in 0 Posts
Default

Hi,

Thanks for the responce. I'm sorry about the lack of code. It's frustrating not being able to do this. I've created a tool that must take the XML output of someone else's work and I need to change it to fit the structure that my system requires(XSLT transformation of XML is the bit that I cannot grasp). The examples I am using are simplified versions of what I need (I didn't want to post huge chunks of code).

I was using the stylesheet above which works well for what I originally specified but I'd like to change it to move <function> all it's attributes and elements to the correct node identified by nodename. The stylesheet is as follows.

Code:
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">
  
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  
  <xsl:key name="k1" match="function" use="@nodename"/>
  
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="functionality[@available = 'true']">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates select="key('k1', parent::node/name)/node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="function"/>

</xsl:stylesheet>
I have two issues one is to move function but not into functionality just into node. I was thinking that <xsl:template match="functionality[@available = 'true']"> would be the line to change to match node or anything but that didn't work. From your comments I'm thinking maybe the change belongs with the first copy function in the stylesheet.

Also the current stylesheet moves the elements of function but not the function tag so I need to address that. I was guessing that may be something to do with parent:: and node() but to be honest, I'm totally lost. I feel like I'm stabbing in the dark.

I'm thinking that instead of tweaking the above stylesheet maybe coming at it again from scratch may be best as the spec is different.

I think I need to learn the basics, although I thought this was the basics.

Thanks for all your help.
ps the choice of <node> tag by me for an example was a bad choice. Too many nodes floating about.
 
Old August 20th, 2009, 06:33 AM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

Here is an adapted stylesheet:
Code:
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">
  
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  
  <xsl:key name="k1" match="function" use="@nodename"/>
  
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="model">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()[not(self::function)]"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="node[functionality[@available = 'true']]">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
      <xsl:apply-templates select="key('k1', name)"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>
Output now is
Code:
<model>
   <concept name="example" uniqueID="10"/>
   <node>
      <name>StratA</name>
      <uniqueID>11</uniqueID>
      <class>static</class>
      <category>user</category>
      <parent>example</parent>
      <parentID>10</parentID>
      <functionality available="false"/>
   </node>
   <node>
      <name>TempA</name>
      <uniqueID>14</uniqueID>
      <class>static</class>
      <category>user</category>
      <parent>example</parent>
      <parentID>10</parentID>
      <functionality available="true"/>
      <function nodename="TempA">
         <loop>
            <name>loop1</name>
         </loop>
         <fork>
            <kind>OR</kind>
         </fork>
         <sub>
            <type>dynamic</type>
         </sub>
      </function>
   </node>
</model>
So we now have one template for 'model' elements that ensure all nodes besides the function elements are processed and we have a template for 'node' elements with a child element 'functionality' where the 'available' attribute is true where we ensure that the corresponding 'function' element is processed. As the only other template we have is the so called identity transformation we are basically copying the 'function' element.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog





Similar Threads
Thread Thread Starter Forum Replies Last Post
Regarding xml-html transformation of an xml string using xslt and javascript suprakash444 XSLT 1 January 12th, 2009 01:23 AM
restructuring xml bobtail XSLT 4 June 18th, 2008 06:48 AM
xml and xsl templates as input to xslt gives xml rameshnarayan XSLT 5 August 3rd, 2005 01:58 AM
merge two xml file and make new xml using xslt ketan XSLT 0 September 21st, 2004 08:48 AM
Merge XML files into a xml file using xslt lxu XML 4 November 6th, 2003 06:01 PM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.