cancel
Showing results for 
Search instead for 
Did you mean: 

Tricky XSLT

Former Member
0 Kudos

Hello all,

here is the sample structure of the import file:

MT_PO
       DT_PO
             DocumentHeader
             ItemHeader
             ItemDetail
       DT_PO
             DocumentHeader
             ItemHeader
             ItemDetail
             ItemDetail
             ItemHeader
             ItemDetail

So, as you see, DocumentHeader is 1:1, ItemHeader is 1:1 and ItemDetail is 1:n cardinality. The problem is that ItemHeader and ItemDetail are on the same level in the structure, and I need transformation to structure like this:

MT_PO
       DT_PO
             DocumentHeader
             Items
                  Item
                          ItemHeader
                          ItemDetail
       DT_PO
             DocumentHeader
             Items
                  Item
                          ItemHeader
                          ItemDetail
                          ItemDetail
                  Item
                          ItemHeader
                          ItemDetail

Following transformation does not work because (of course) for each ItemHeader it reads all ItemDetail in the parrent node:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
	<xsl:template match="/">
		<MT_PO>
			<xsl:for-each select="MT_PO/DT_PO">
				<DT_PO>
				<xsl:copy-of select="DocumentHeader"/>
				<Items>
					<xsl:for-each select="ItemHeader">
					<Item>
						<xsl:copy-of select="."/>
						<xsl:for-each select="../ItemDetail">
							<xsl:copy-of select="."/>
						</xsl:for-each>
					</Item>
					</xsl:for-each>
				</Items>
				</DT_PO>
			</xsl:for-each>
		</MT_PO>
	</xsl:template>
</xsl:stylesheet>

Does anybody has idea how to do that?

Thanks in advance!

Best regards,

Ivan

Accepted Solutions (1)

Accepted Solutions (1)

Former Member
0 Kudos

I would recommend looking into writing a user-defined function in Java that will allow you to perform "context changes", and manipulate the parent-child structure of the target message.

Hopefully this blog will provide a good intro to the topic:

/people/harrison.holland5/blog/2006/12/08/mapping-context-changes-in-xi

You can use two functions calls in a "queue" user-defined function.

(1) result.addValue();

(2) result.addContextChange();

Former Member
0 Kudos

Thank you for your help. You think there is no way to do it with plain XSLT?

Regards,

Ivan

Former Member
0 Kudos

After looking at your requirements again, it seems like you may be able to avoid a custom java function by using the splitByValue graphical mapping function, which is pretty standard. Assuming that you are not going to be doing much other manipulation of the source fields. The splitByValue function will simply take the source field and perform the context change for you everytime it finds a new instance of the child-node. You do have to make sure to map the parent 1:1 to each other in order for this to work.

Former Member
0 Kudos

I

Message was edited by:

Harrison Holland

Former Member
0 Kudos

Hi Harrison,

could you share some example, with source code of the mapping?

Tnx,

Ivan

Former Member
0 Kudos

First, check out <a href="http://help.sap.com/saphelp_nw04/helpdata/en/21/3bb8c495125e4eb5969f0377885fe0/content.htm">this link</a>. That gives a good simple example of how to use splitByValue.

Now, for your scenario to take a source message like this:

MT_PO
       DT_PO
             DocumentHeader
             ItemHeader
             ItemDetail
       DT_PO
             DocumentHeader
             ItemHeader
             ItemDetail
             ItemDetail
             ItemHeader
             ItemDetail

and produce a target message like this:

MT_PO
       DT_PO
             DocumentHeader
             Items
                  Item
                          ItemHeader
                          ItemDetail
       DT_PO
             DocumentHeader
             Items
                  Item
                          ItemHeader
                          ItemDetail
                          ItemDetail
                  Item
                          ItemHeader
                          ItemDetail

I would do a mapping something like this:

(source) MT_PO :: (target) MT_PO

(source) DT_PO :: (target) DT_PO

(source) DocumentHeader :: (target) DocumentHeader

(source) ItemHeader : splitByValue : (target) \Items\item\ItemHeader

(source) ItemDetail & ItemHeader: UDF : (target) \Items\item\ItemDetail

The user defined function should be defined as a type queue with two inputs, the ItemDetail and the ItemHeader. Then you will loop through the values as shown in the first blog I linked regarding <a href="/people/harrison.holland5/blog/2006/12/08/mapping-context-changes-in-xi changes</a> and perform a result.addContextChange() everytime a new header comes in, but only pass back the values of ItemDetail with result.addValue().

Good luck

Former Member
0 Kudos

Thank you for the tip... unfortunately, I'm not really used to graphical mapping in XI, so probably, I'll go with java mapping.

Regards,

Ivan

Former Member
0 Kudos

Hi,

fortunately, there is an XSLT solution for the problem

[code]<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:output indent="yes"/>

<xsl:template match="/">

<MT_PO>

<xsl:apply-templates select="MT_PO/DT_PO"/>

</MT_PO>

</xsl:template>

<xsl:template match="DT_PO">

<DT_PO>

<xsl:copy-of select="MT_PO/DT_PO/DocumentHeader"/>

<Items>

<xsl:for-each select="ItemHeader">

<Item>

<xsl:copy-of select="."/>

<xsl:variable name="headerId" select="generate-id(.)"/>

<xsl:for-each select="following-sibling::ItemDetail[generate-id(preceding-sibling::ItemHeader[1])=$headerId]">

<xsl:copy-of select="."/>

</xsl:for-each>

</Item>

</xsl:for-each>

</Items>

</DT_PO>

</xsl:template>

</xsl:stylesheet>[/code]

Regards,

Ivan

Answers (0)