Introduction
This is the second part of the two-part series on achieving file content conversion for deep structure with XSLT. The first part covered the approach to flatten a deep structure XML prior to receiver FCC conversion.
This second part will focus on deepening a flat XML after FCC conversion at a sender channel.
Source Code and Explanation
The full source code, example input and output files are available in the GitHub repository listed in the first part of this series.
Prerequisite: To deepen an XML, the segments that will be structured in a parent-child relationship needs to have a field with the same value on both the parent and the child.
The logic comprises of three different types of XSLT template match sections.
1. Selecting child segments at root element level
At root element level, the segments that should be immediate child segments are explicitly selected.
There are two different versions of the code as this is dependent on the FCC configuration of the sender channel. For NFS/FTP channels configured with ignoreRecordsetName = true, the generated XML from FCC will not have the Recordset segment as a child of the root element. Due to slight behavioral difference of the MessageTransformBean module, it does not support the above parameter and will always have a Recordset segment.
In the example below, segments Header, Delivery and Footer are "designated" as direct child elements of the root element (or Recordset element.)
With Recordset | <!-- At the root element level, manually select the child segments --><xsl:template match="*[local-name() = 'MT_Deep']"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy></xsl:template><xsl:template match="Recordset"> <xsl:copy> <xsl:apply-templates select="Header"/> <xsl:apply-templates select="Delivery"/> <xsl:apply-templates select="Footer"/> </xsl:copy></xsl:template> |
Without Recordset | <!-- At the root element level, manually select the child segments --><xsl:template match="*[local-name() = 'MT_Deep']"> <xsl:copy> <xsl:apply-templates select="Header"/> <xsl:apply-templates select="Delivery"/> <xsl:apply-templates select="Footer"/> </xsl:copy></xsl:template> |
2. Deepening segments with parent-child relationship
For subsequent segments with parent-child relationship, first the key field in the parent segment is saved in a variable, then the immediate child fields are copied, finally, child segments which have its key field matching the parent are selected.
The similar match template logic is repeated as required for all parent segments that will contain child segments.
<xsl:template match="Delivery"> <!-- (1) - Save parent key value to be used to select corresponding child segments --> <xsl:variable name="deliveryno" select="DeliveryNo"/> <xsl:copy> <!-- (2) - Select the child elements that are fields --> <xsl:apply-templates select="*[not(*)]"/> <!-- (3) - Select the child segments which have matching value as parent key value --> <xsl:apply-templates select="../Order[DeliveryNo=$deliveryno]"/> </xsl:copy></xsl:template>
3. Identity transformation for all other attributes and nodes
Finally, for all other attributes and nodes, identity (1-1) transformation is applied.
<!-- Match all other attributes (@*) and other node types (elements, comments) --><!-- Copy the current element, and select child attributes and nodes --><xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy></xsl:template>
Example Scenario
We will reuse the example structure used in the first part. The goal is to achieve creation of a deep XML payload from the flat file with deep/nested structure.
The input file will be a shipment file which contains all the details for the shipment. Each shipment file can have multiple deliveries, each delivery in turn can have multiple orders, and each order multiple items. Each segment occurs under the parent segment, therefore indicating a deep/nested structure.
Segment Name | Parent Segment | Type Indicator | Occurrence |
---|---|---|---|
Header | - | H | 1 |
Delivery | - | D | 1 - unbounded |
Order | Delivery | O | 1 - unbounded |
OrderText | Order | T | 0 - 1 |
Item | Order | I | 1 - unbounded |
Footer | - | F | 1 |
Design
This article will only focus on the sections that are required to enable this XSLT approach, therefore common steps for design and configuration will not be covered.
Source structure definition
The data type for the source structure will be defined as a deep XML schema. No flat DT definition that is required for FCC needs to be defined. Below are the sample deep XML schema representation of the input format, depending on whether Recordset is ignored during FCC or not.
For the remainder of the example, the design will be based on the structure with Recordset as it will be using the MessageTransformBean module in a sender SFTP channel.
Without Recordset | With Recordset |
---|---|
![]() | ![]() |
Message Mapping
Create a normal graphical message mapping to map the deep source structure above to the intended target structure.
Import XSLT code
Zip the XSL file and import it to ESR as an Imported Archive.
Operation Mapping
Typically the XSLT mapping will be used as the first step of the Operation Mapping that will deepen the XML generated from FCC. Then the message mapping will map the deep source to intended target.
Step | Mapping Type | Name |
---|---|---|
1 | XSL | Flat2Deep_WithRecordset |
2 | Message Mapping | <Message Mapping object created above> |
Below is a sample execution of the first step of the OM to deepen the XML that was created by the FCC at sender. The result shows that the XSLT have deepened the XML to the intended structure.
Note: As seen in the test tab, the XML output of the sender FCC is not a valid XML document when validated against the original data type definition (the segments indicated in red icons.) It does not matter as the main purpose of the XML is to be further deepened by the XSLT.
Configuration
The normal configuration steps are performed. At the sender channel, specify the FCC parameters. The following screenshot shows the FCC parameters using MessageTransformBean on an SFTP channel.
Note: The recordset structure order needs to be set to variable (xml.recordsetStructureOrder = var) because due to input flat file having deep structure, the segments are not in an ascending order.
Testing Results
After execution of the interface, we can view the logs to see the content of the original file prior to sender FCC.
After the XSLT transformation (and using a 1-1 graphical message mapping) the target payload in the receiver proxy is displayed. As we can see below, the XML generated has a deeply nested structure.
Conclusion
Similar to the first part, we have achieved a deep XML structure after sender FCC with a relatively simple XSLT logic. However, note that the logic cannot be coded so that it can be generically reused because the exact parent-child relationship needs to be specified.