Click here to Skip to main content
Click here to Skip to main content

Tagged as

Applying XSL Transformations

, 31 Mar 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
Setup: A data from one module is passed into another so that Module 1 takes a (flat) file as input and feeds an xml structure into Module 2. While Module 2 is internal, Module 1 is designed to handle different customer inputs into the system. However, it is not always possible to generate the...
Setup:
A data from one module is passed into another so that Module 1 takes a (flat) file as input and feeds an xml structure into Module 2. While Module 2 is internal, Module 1 is designed to handle different customer inputs into the system. However, it is not always possible to generate the expected Module 2 xml structure from the flat file in one step.
 
|Customer| -> file -> |Module 1| -> xml(products-product-detail) -> |Module 2|
 
There are at least two ways to accomodate the (multi-step) translation process:
  • Add re-parsing logic directly in the code - not smart enough as we do not know what input formats will need to be supported by Module 1 in the future
  • Apply customizable xsl transformations - providing decoupled solution with a reliable techology
 
For example:

Given a list of details, we can group them into product(s) in order to conform to the expected Module 2 hierarchy.
 
List of details:
<?xml version='1.0' encoding="iso-8859-1"?>
<PRODUCTS>
	<DETAIL>
		<DESC>D1</DESC>
		<PRODUCT_NAME>APL</PRODUCT_NAME>
	</DETAIL>
	<DETAIL>	
		<DESC>D2</DESC>
		<PRODUCT_NAME>ANL</PRODUCT_NAME>
	</DETAIL>
	<DETAIL>
		<DESC>D3</DESC>
		<PRODUCT_NAME>APL</PRODUCT_NAME>
	</DETAIL>
</PRODUCTS> 
 
List of details grouped by product:
<?xml version='1.0' encoding="iso-8859-1"?>
<PRODUCTS>
	<PRODUCT>
		<PRODUCT_NAME>ANL</PRODUCT_NAME>
		<DETAIL>	
			<DESC>D2</DESC>
			<PRODUCT_NAME>ANL</PRODUCT_NAME>
		</DETAIL>
	</PRODUCT>
	<PRODUCT>
		<PRODUCT_NAME>APL</PRODUCT_NAME>
		<DETAIL>
			<DESC>D1</DESC>
			<PRODUCT_NAME>APL</PRODUCT_NAME>
		</DETAIL>
		<DETAIL>
			<DESC>D3</DESC>
			<PRODUCT_NAME>APL</PRODUCT_NAME>
		</DETAIL>
	</PRODUCT>
</PRODUCTS> 
 
Thumbs Up | :thumbsup: In order to achieve this grouping, we can use the Muenchian method as described here:

http://www.jenitennison.com/xslt/grouping/muenchian.html[^]
<!-- GROUPING USING THE MUENCHIAN METHOD: http://www.jenitennison.com/xslt/grouping/muenchian.html -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="xml" indent="yes" encoding="iso-8859-1"/>
 
	<xsl:key name="details-by-product" match="DETAIL" use="PRODUCT_NAME" />
 
	<xsl:template match="PRODUCTS">
		<PRODUCTS>
		<xsl:for-each select="DETAIL[count(. | key('details-by-product', PRODUCT_NAME)[1]) = 1]">
			<xsl:sort select="PRODUCT_NAME" />
			<PRODUCT>
				<PRODUCT_NAME><xsl:value-of select="PRODUCT_NAME"/></PRODUCT_NAME>
				<xsl:for-each select="key('details-by-product', PRODUCT_NAME)">
					<DETAIL>
					<xsl:apply-templates/>
					</DETAIL>
				</xsl:for-each>
			</PRODUCT>
		</xsl:for-each>
		</PRODUCTS>
	</xsl:template>
 
	<xsl:template match="@*|node()">
	  <xsl:copy>
		<xsl:apply-templates select="@*|node()"/>
	  </xsl:copy>
	</xsl:template>
</xsl:stylesheet>
See also:
 

    XSL Transformations (XSLT):  http://www.w3.org/TR/xslt[^]
    Grouping With XSLT 2.0:  http://www.xml.com/pub/a/2003/11/05/tr.html[^]
    Dave Pawson's Grouping page:  http://www.dpawson.co.uk/xsl/sect2/N4486.html[^]
 

Thumbs Up | :thumbsup: In order to test the transformation, we can initiate the xslt in a script as described here:

http://msdn.microsoft.com/en-us/library/ms762796(VS.85).aspx[^]
//Initiate XSLT in a script: http://msdn.microsoft.com/en-us/library/ms762796(VS.85).aspx
var oArgs = WScript.Arguments;
 
if (oArgs.length == 0)
{
    WScript.Echo ("Usage : cscript xslt.js xml xsl");
    WScript.Quit();
}
xmlFile = oArgs(0) + ".xml";
xslFile = oArgs(1) + ".xsl";
 
var xsl = new ActiveXObject("MSXML2.DOMDOCUMENT.6.0");
var xml = new ActiveXObject("MSXML2.DOMDocument.6.0");
xml.validateOnParse = false;
xml.async = false;
xml.load(xmlFile);
 
if (xml.parseError.errorCode != 0)
    WScript.Echo ("XML Parse Error : " + xml.parseError.reason);
 
xsl.async = false;
xsl.load(xslFile);
 
if (xsl.parseError.errorCode != 0)
    WScript.Echo ("XSL Parse Error : " + xsl.parseError.reason);
 
try
{
    WScript.Echo (xml.transformNode(xsl.documentElement));
}
catch(err)
{
    WScript.Echo ("Transformation Error : " + err.number + "*" + err.description);
}
 
Thumbs Up | :thumbsup: In order to apply the transformation at run-time, we can use the XslCompiledTransform as described here:

http://msdn.microsoft.com/en-us/library/system.xml.xsl.xslcompiledtransform_members.aspx[^]
// XslCompiledTransform: http://msdn.microsoft.com/en-us/library/system.xml.xsl.xslcompiledtransform_members.aspx
// Note: Exception handling is outside of the scope of this tip
// Note: Xml results are loaded into memory and do not have to be written into the file system, if there is no need for it.
private string ApplyStylesheet(string xml, string stylesheet)
{
	if (!string.IsNullOrEmpty(stylesheet))
	{
		//Configure the reader to skip all forms of validation
		XmlReaderSettings settingsReader = new XmlReaderSettings();
		settingsReader.ValidationType = ValidationType.None;
		settingsReader.ProhibitDtd = false;
		settingsReader.XmlResolver = null;
 
		StringReader strReader = new StringReader(xml);
		XmlReader xmlReader = XmlReader.Create(strReader, settingsReader);
		// use XmlNodeReader xmlReader = new XmlNodeReader(xml); if xml is XmlNode (not a string)

		StringWriter stringWriter = new StringWriter();
		XmlTextWriter xmlWriter = new XmlTextWriter(stringWriter);
 
		XslCompiledTransform xslt = new XslCompiledTransform();
		xslt.Load(stylesheet);
		xslt.Transform(xmlReader, xmlWriter);
 
		xmlWriter.Close();
		xmlReader.Close();
 
		return stringWriter.ToString();
	}
	return xml;
}
 
Good luck!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ilka Guigova
Software Developer
Canada Canada

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 31 Mar 2010
Article Copyright 2010 by Ilka Guigova
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid