XSLT: tips and tricks: how to generate node1, node2, …, nodeN

This time let me show you how to generate several similar XML elements like name1, name2, name3, …, nameN.

Input:

<?xml version="1.0" encoding="utf-8"?>
<names>
	<name>John Smith</name>
	<name>Bill Murray</name>
	<name>Anne Williams</name>
	<name>Henry Ford</name>
</names>

Expected output:

<?xml version="1.0" encoding="utf-8"?>
<root>
	<NAME1>John Smith</NAME1>
	<NAME2>Bill Murray</NAME2>
	<NAME3>Anne Williams</NAME3>
	<NAME4>Henry Ford</NAME4>
</root>

Almost always developers are trying to create XSLT like this:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:template match="/">
		<root>
			<xsl:if test="names/name[1]">
				<NAME1>
					<xsl:value-of select="names/name[1]"/>
				</NAME1>
			</xsl:if>
			<xsl:if test="names/name[2]">
				<NAME2>
					<xsl:value-of select="names/name[2]"/>
				</NAME2>
			</xsl:if>
			...
			<xsl:if test="names/name[N]">
				<NAMEN>
					<xsl:value-of select="names/name[N]"/>
				</NAMEN>
			</xsl:if>
		</root>
	</xsl:template>
</xsl:stylesheet>

But there is a short and elegant way:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
		<root>
			<xsl:for-each select="names/name">
				<xsl:element name="{concat('NAME', position())}">
					<xsl:value-of select="."/>
				</xsl:element>
			</xsl:for-each>
		</root>
    </xsl:template>
</xsl:stylesheet>

Gennady Kim

Building Specs/Designs/MRGs automatically (from XML data)

I’ve enhanced a little the XSLT described here. First version could build the structures only, and we decided to add DDF files (Gentran/GIS/SI model format) as the formats (X12, EDIFACT, IDocs, etc) libraries. Now we can get not only the structure, but also descriptions, data types, optional/mandatory and qualifiers with descriptions. These specs were built automatically, I added colored backgrounds only:

X12:
spec_x12
Continue reading

webMethods: how to create an XSD schema (2)

UPD (22 Oct 2014): The original createXSD doesn’t work properly in some cases, so now I’m using both createXSD and my XSLT

My previous post about XSL for converting NDF into XSD was a try to “reinvent a wheel”. There is a native way to do the same in WM – WmPublic/pub/schema/createXSD 🙂 I didn’t know about it, but now I do know. Sometimes I have to change generated XSDs but anyway I think it’s easier to use built-in methods. Well, lesson learned – ask an expert first 🙂

Gennady Kim

webMethods: how to create an XSD schema from NDF structure definition file(s)

One of our clients uses webMethods for EAI. I’m not a big expert in webMethods (WM), I know only my part – EAI, partners setup, testing and map development. WM uses it’s own way for the structures definition. I cannot use it in my work, I need standard XSD or at least DTD files. So I decided to find a way to somehow export or convert those NDF files into XSD. These NDF files are in XML format so I decided to develop an XSLT to transform them into XSD. Now I’d like to share it with you. Mention should be made that I’m not an WM expert – maybe this XSLT works for only these particular NDF files and won’t work for all the others, but if you have a similar task – just give it a try and please let me know if it works for you or not. Sometimes there are more than just one NDF file to describe the structure – my XSLT supports such cases (a variable “base” at the beginning is for the folder’s name). It might require some explanation. If you have problems with it – I could try to help, feel free to contact me (there is a contact form)

Continue reading

SAP IDoc FlatFile to XML converter

One of our projects is to create/change hundreds of maps and switch from SAP IDoc flat files to XML files. All of the test files are in the FlatFile format, so I decided to create a converter to translate them into XML. It’s clear that this task requires some kind of a library with the IDoc documents structures description (hierarchy, records, positions, etc). I decided to use IDoc DDFs (format used by IBM/Sterling Map Editor for creating structures – you can download them from GIS/SI or just use existing GIS/SI map) and XSLT for it, and several hours later can convert my test data from FF to XML in seconds:

Just an idea.

PS I don’t have access to SAP at the moment so I cannot use it for XML Idoc generation. Also, I have appropriate I/Os for existing IDocs so it made sense to convert these existing files.

PPS it’s clear that an XML->FF converter is easier to develop – you don’t need to care about the hierarchy.

Here is my XSLT code. It doesn’t work without appropriate DDF file so if you have no idea about XSL/DDF you won’t be able to use it. It’s for those who is experienced but lazy – just like me 🙂 Continue reading

Auxiliary XSLT for building specs/MRGs for XML

Here is a simple XSLT which helps me to “extract” the structure from XML file. I use it for building specifications/designs/MRGs in MS Excel format:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:variable name="default_indent" select="'& #x9;'" />
	<xsl:template match="/">
		<xsl:for-each select=".">
			<xsl:call-template name="processElement" />
		</xsl:for-each>
	</xsl:template>

	<xsl:template name="processElement">
		<xsl:param name="indent" select="''" />

		<xsl:value-of select="$indent"/>
		<xsl:value-of select="name()"/>
		<xsl:text>& #xA;</xsl:text>
		
		<xsl:for-each select="@*">
			<xsl:value-of select="concat($indent, $default_indent)"/>
			<xsl:value-of select="'@'"/>
			<xsl:value-of select="name()"/>
			<xsl:text>& #xA;</xsl:text>
		</xsl:for-each>

		<xsl:if test="count(./*) != 0">
			<xsl:for-each select="./*">
				<xsl:call-template name="processElement">
					<xsl:with-param name="indent">
						<xsl:value-of select="concat($indent, $default_indent)"/>
					</xsl:with-param>
				</xsl:call-template>
			</xsl:for-each>
		</xsl:if>
	</xsl:template>
</xsl:stylesheet>

Continue reading

XSLT: tips and tricks: how to process node1, node2, …, nodeN

Very often people don’t understand how to process structures like

<forecast>
      <qty1>1</qty1>
      <date1>01022014</date1>

      <qty2>1</qty2>
      <date2>01022014</date2><qtyN>1</qtyN>
      <dateN>01022014</dateN>
</forecast>

They create N templates or repeat the same code again and again, changing the names: qty1/date1, qty2/date2, … But all they need to do is to use the name() function, like this:

<xsl:for-each select="forecast/*[ starts-with(name(), 'qty') ]">

I think the idea is self explanatory…

Gennady Kim

XSLT and the math functions

It’s interesting that a lot of math functions in XSLT 1.0 (multiplication, rounding, div, …) depend on the XSLT processor/platform and might produce different outputs. So, you might need to leave all of these operations to the processes outside of XSLT, or at least test them carefully.

Just an example:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:template match="/">
            =<xsl:value-of select="format-number(round( 0.005 * 100) div 100, '0.00')" />
            =<xsl:value-of select="format-number(( 0.005 * 100) div 100, '0.00')" />
            =<xsl:value-of select="format-number(round( 9999.005 * 100) div 100, '0.00')" />
            =<xsl:value-of select="format-number(( 9999.005 * 100) div 100, '0.00')" />
      </xsl:template>
</xsl:stylesheet>

Outputs:
Continue reading

How to “debug” XSLTs

I don’t use any specific tool to test and debug XSLT files. In 90%+ cases the problem is with the XPath and I need to find the exact place where it becomes wrong. So, I use the “count()” function and (sometimes) xsl:comment, like this:

<xsl:comment>
	-<xsl:value-of select="count(aaa)"/>
	-<xsl:value-of select="count(aaa/bbb)"/>
	-<xsl:value-of select="count(aaa/bbb/ccc)"/>
	-<xsl:value-of select="count(aaa/bbb/ccc[@ddd = 'eee'])"/>
</xsl:comment>

If I see something like

<!--
  -1
  -1
  -3
  -0
-->

in the output, it’s clear that the problem with the XPath condition in the brackets.

Gennady Kim

XSLT: copy XML with some minor changes

Today I worked on an XSLT style which copies XML into the same XML but with couple of changes. There were hundreds of elements so I created the following XSLT (example):

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<xsl:template match="/">
		<xsl:for-each select="*">
			<xsl:call-template name="copyElement"/>
		</xsl:for-each>
	</xsl:template>

	<xsl:template name="copyElement">
		<xsl:variable name="segName" select="name(.)" />
		<xsl:element name="{$segName}">
			<xsl:for-each select="@*">
				<xsl:attribute name="{name(.)}">
					<xsl:value-of select="."/>
				</xsl:attribute>
			</xsl:for-each>

			<xsl:choose>
				<xsl:when test="contains($segName, 'SegmentName')">
					<!-- custom code -->
				</xsl:when>
			</xsl:choose>


			<xsl:if test="normalize-space(text()) != ''">
				<xsl:value-of select="normalize-space(.)"/>
			</xsl:if>

			<xsl:for-each select="*">
				<xsl:call-template name="copyElement"/>
			</xsl:for-each>
		</xsl:element>
	</xsl:template>
</xsl:stylesheet>

Gennady Kim