Appboard/2.4/builder/data sources/web service/xml-xslt-tutorial: Difference between revisions
imported>Jason.nicholls No edit summary |
imported>Jason.nicholls No edit summary |
||
(3 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
{{DISPLAYTITLE:XML XSLT Tutorial}} | {{DISPLAYTITLE:XML XSLT Tutorial}} | ||
[[Category:AppBoard 2.4]] | |||
Included with the distribution under <tt>${application.home}/stylesheets/</tt> are some example XSL files that correspond to a variety of sample XML files included under <tt>${application.home}/data/pkg/sample/</tt>. You can also use these as guides when you create your own XSL files. To see the result of a transformation (and to help debug custom XSL files), you can use the 'Transform' option when running portal.bat or portal.sh. To do this, in the command line navigate to <tt>[INSTALL_HOME]/server/bin/</tt> and run <tt>portal Transform</tt> to display the possible arguments you can use when doing an XML/XSL transform. | |||
An example of a standard command: | An example of a standard command: | ||
Line 10: | Line 8: | ||
This will take the input XML file and the indicated XSL file, do a transformation, and output the resulting XML to output.xml | This will take the input XML file and the indicated XSL file, do a transformation, and output the resulting XML to output.xml | ||
== Tutorial == | |||
Using <tt>${application.home}/data/pkg/sample/hierarchy/hierarchy.xml</tt> as an example, this tutorial builds the XSL Transformation in a step-by-step manner. It will be helpful in following the tutorial to have the source XML and the target XML in separate windows or printed out. | |||
1. Review the basic structure of your source XML. | 1. Review the basic structure of your source XML. | ||
Line 34: | Line 36: | ||
</xsl:stylesheet> | </xsl:stylesheet> | ||
</code> | </code> | ||
4. Identify an element that has only a single instance and is the parent of all of the data elements you wish to capture as Entities. In many cases, that would be the root element ("rspec" in this example), but because there is only one "computeResource" instance we can reference it directly as "/rspec/computeResource". | 4. Identify an element that has only a single instance and is the parent of all of the data elements you wish to capture as Entities. In many cases, that would be the root element ("rspec" in this example), but because there is only one "computeResource" instance we can reference it directly as "/rspec/computeResource". | ||
5. Add a match template for that data element that emits the "result" element and definitions for the entities we will capture: | 5. Add a match template for that data element that emits the "result" element and definitions for the entities we will capture: | ||
Line 61: | Line 66: | ||
</code> | </code> | ||
: This only works for single instance elements. If there were multiple "computeResource" elements we would start with "/rspec" even if we weren't capturing an Entity corresponding to "computeResource" instances. | : This only works for single instance elements. If there were multiple "computeResource" elements we would start with "/rspec" even if we weren't capturing an Entity corresponding to "computeResource" instances. | ||
6. Add do-nothing templates for the peers of "computeResource" that we plan to ignore: | 6. Add do-nothing templates for the peers of "computeResource" that we plan to ignore: | ||
Line 68: | Line 74: | ||
<xsl:template match="/rspec/lifetime"/> | <xsl:template match="/rspec/lifetime"/> | ||
</code> | </code> | ||
7. Add a match template for the "node" elements to construct "record" elements for each instance: | 7. Add a match template for the "node" elements to construct "record" elements for each instance: | ||
Line 79: | Line 87: | ||
</code> | </code> | ||
: Note the use of "@id" to extract an attribute of the "node" element and the use of "address" to extract the value in a child element. | : Note the use of "@id" to extract an attribute of the "node" element and the use of "address" to extract the value in a child element. | ||
8. Add a match template for the "networkInterface" elements. Note that we use "node/networkInterface" since that is the relative "path" from our "computeResource" element. | 8. Add a match template for the "networkInterface" elements. Note that we use "node/networkInterface" since that is the relative "path" from our "computeResource" element. | ||
Line 90: | Line 99: | ||
</xsl:template> | </xsl:template> | ||
</code> | </code> | ||
9. Place these after the template for "computeResource" and then add xsl:apply-templates elements to reference them to construct children of the "entity" elements in the output: | 9. Place these after the template for "computeResource" and then add xsl:apply-templates elements to reference them to construct children of the "entity" elements in the output: | ||
Line 132: | Line 143: | ||
</code> | </code> | ||
: Now we have a working XSL Transform that defines our two entities and constructs all of the records. But, the hierarchy of the data gives us an association that we aren't capturing. | : Now we have a working XSL Transform that defines our two entities and constructs all of the records. But, the hierarchy of the data gives us an association that we aren't capturing. | ||
10. Define the association in the "Nodes" entity: | 10. Define the association in the "Nodes" entity: | ||
Line 139: | Line 151: | ||
</associations> | </associations> | ||
</code> | </code> | ||
11. Add an attribute to "NICs" to store the key: | 11. Add an attribute to "NICs" to store the key: | ||
Line 150: | Line 164: | ||
</entity> | </entity> | ||
</code> | </code> | ||
12. Use "../@id" in an xsl:value-of to add a "NodeId" value to the instances of "NICs". | 12. Use "../@id" in an xsl:value-of to add a "NodeId" value to the instances of "NICs". | ||
Latest revision as of 07:04, 14 December 2013
Included with the distribution under ${application.home}/stylesheets/ are some example XSL files that correspond to a variety of sample XML files included under ${application.home}/data/pkg/sample/. You can also use these as guides when you create your own XSL files. To see the result of a transformation (and to help debug custom XSL files), you can use the 'Transform' option when running portal.bat or portal.sh. To do this, in the command line navigate to [INSTALL_HOME]/server/bin/ and run portal Transform to display the possible arguments you can use when doing an XML/XSL transform.
An example of a standard command:
portal Transform -in example.xml -xsl example.xsl -out output.xml
This will take the input XML file and the indicated XSL file, do a transformation, and output the resulting XML to output.xml
Tutorial
Using ${application.home}/data/pkg/sample/hierarchy/hierarchy.xml as an example, this tutorial builds the XSL Transformation in a step-by-step manner. It will be helpful in following the tutorial to have the source XML and the target XML in separate windows or printed out.
1. Review the basic structure of your source XML.
- This example consists of a single root node ("rspec") with some descriptive child elements one of which ("computeResource") contains nested information on compute nodes which contain further information including network interfaces. In outline form:
- rspec
- aggregate
- description
- lifetime
- computeResource
- node
- networkInterface
- node
- networkInterface
- node
- networkInterface
- node
- rspec
2. Identify the data elements you want to transform into AppBoard Entities.
- In this case, we are interested in nodes and networkInterfaces which we will call Nodes and NICs.
3. Begin the XSLT with the xsl:stylesheet element and an xsl:output element:
[xml,N]
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" version="1.0">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
</xsl:stylesheet>
4. Identify an element that has only a single instance and is the parent of all of the data elements you wish to capture as Entities. In many cases, that would be the root element ("rspec" in this example), but because there is only one "computeResource" instance we can reference it directly as "/rspec/computeResource".
5. Add a match template for that data element that emits the "result" element and definitions for the entities we will capture:
[xml,N]
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" version="1.0">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:template match="/rspec/computeResource">
<result xmlns="http://www.edgeti.com/xml-data">
<entity name="Nodes">
<attributes>
<attribute primaryKey="true">NodeId</attribute>
<attribute>Address</attribute>
</attributes>
</entity>
<entity name="NICs">
<attributes>
<attribute primaryKey="true">NICId</attribute>
<attribute>Address</attribute>
</attributes>
</entity>
</result>
</xsl:template>
</xsl:stylesheet>
- This only works for single instance elements. If there were multiple "computeResource" elements we would start with "/rspec" even if we weren't capturing an Entity corresponding to "computeResource" instances.
6. Add do-nothing templates for the peers of "computeResource" that we plan to ignore:
[xml,N]
<xsl:template match="/rspec/aggregate"/>
<xsl:template match="/rspec/description"/>
<xsl:template match="/rspec/lifetime"/>
7. Add a match template for the "node" elements to construct "record" elements for each instance:
[xml,N]
<template match="node">
<record>
<value name="NodeId"><value-of select="@id"/></value>
<value name="Address"><value-of select="address"/></value>
</record>
</template>
- Note the use of "@id" to extract an attribute of the "node" element and the use of "address" to extract the value in a child element.
8. Add a match template for the "networkInterface" elements. Note that we use "node/networkInterface" since that is the relative "path" from our "computeResource" element.
[xml,N]
<xsl:template match="node/networkInterface">
<record>
<value name="NICId"><xsl:value-of select="@id"/></value>
<value name="Address"><xsl:value-of select="ipAddress"/></value>
<value name="NodeId"><xsl:value-of select="../@id"/></value>
</record>
</xsl:template>
9. Place these after the template for "computeResource" and then add xsl:apply-templates elements to reference them to construct children of the "entity" elements in the output:
[xml,N]
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" version="1.0">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:template match="/rspec/computeResource">
<result xmlns="http://www.edgeti.com/xml-data">
<entity name="Nodes">
<attributes>
<attribute primaryKey="true">NodeId</attribute>
<attribute>Address</attribute>
</attributes>
<xsl:apply-templates select="node"/>
</entity>
<entity name="NICs">
<attributes>
<attribute primaryKey="true">NICId</attribute>
<attribute>Address</attribute>
</attributes>
<xsl:apply-templates select="node/networkInterface"/>
</entity>
</result>
</xsl:template>
<xsl:template match="/rspec/aggregate"/>
<xsl:template match="/rspec/description"/>
<xsl:template match="/rspec/lifetime"/>
<template match="node">
<record>
<value name="NodeId"><value-of select="@id"/></value>
<value name="Address"><value-of select="address"/></value>
</record>
</template>
<xsl:template match="node/networkInterface">
<record>
<value name="NICId"><xsl:value-of select="@id"/></value>
<value name="Address"><xsl:value-of select="ipAddress"/></value>
</record>
</xsl:template>
</xsl:stylesheet>
- Now we have a working XSL Transform that defines our two entities and constructs all of the records. But, the hierarchy of the data gives us an association that we aren't capturing.
10. Define the association in the "Nodes" entity:
[xml,N]
<associations>
<association fromKey="NodeId" toEntity="NICs" toKey="NodeId"/>
</associations>
11. Add an attribute to "NICs" to store the key:
[xml,N]
<entity name="NICs">
<attributes>
<attribute primaryKey="true">NICId</attribute>
<attribute>Address</attribute>
<attribute>NodeId</attribute>
</attributes>
</entity>
12. Use "../@id" in an xsl:value-of to add a "NodeId" value to the instances of "NICs".
[xml,N]
<xsl:template match="node/networkInterface">
<record>
<value name="NICId"><xsl:value-of select="@id"/></value>
<value name="Address"><xsl:value-of select="ipAddress"/></value>
<value name="NodeId"><xsl:value-of select="../@id"/></value>
</record>
</xsl:template>
Resulting XSL:
[xml,N]
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" version="1.0">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:template match="/rspec/computeResource">
<result xmlns="http://www.edgeti.com/xml-data">
<entity name="Nodes">
<attributes>
<attribute primaryKey="true">NodeId</attribute>
<attribute>Address</attribute>
</attributes>
<associations>
<association fromKey="NodeId" toEntity="NICs" toKey="NodeId"/>
</associations>
<xsl:apply-templates select="node"/>
</entity>
<entity name="NICs">
<attributes>
<attribute primaryKey="true">NICId</attribute>
<attribute>Address</attribute>
<attribute>NodeId</attribute>
</attributes>
<xsl:apply-templates select="node/networkInterface"/>
</entity>
</result>
</xsl:template>
<xsl:template match="/rspec/aggregate"/>
<xsl:template match="/rspec/description"/>
<xsl:template match="/rspec/lifetime"/>
<template match="node">
<record>
<value name="NodeId"><value-of select="@id"/></value>
<value name="Address"><value-of select="address"/></value>
</record>
</template>
<xsl:template match="node/networkInterface">
<record>
<value name="NICId"><xsl:value-of select="@id"/></value>
<value name="Address"><xsl:value-of select="ipAddress"/></value>
<value name="NodeId"><xsl:value-of select="../@id"/></value>
</record>
</xsl:template>
</xsl:stylesheet>