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:
='1.0'="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:
='1.0'="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>
:thumbsup: In order to achieve this grouping, we can use the Muenchian method as described here:
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[
^]
: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[
^]
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);
}
: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[
^]
private string ApplyStylesheet(string xml, string stylesheet)
{
if (!string.IsNullOrEmpty(stylesheet))
{
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);
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!