2

I want to output an xml based on some conditions.

Here is my input XML

<YIORDER01>
    <IDOC>
        <E1ORHDR>
            <E1OROPR>
                <VORNR>0010</VORNR>
                <E1OROPR_MAT>
                    <MATNR>M0003-01</MATNR>
                    <YE1OROPR_MAT>
                        <STTXT>REL</STTXT>
                        <MTART>ZPAR</MTART>
                        <POSTP>L</POSTP>
                    </YE1OROPR_MAT>
                </E1OROPR_MAT>
                <E1OROPR_MAT>
                    <MATNR>M0003-01</MATNR>
                    <YE1OROPR_MAT>
                        <STTXT>REL</STTXT>
                        <MTART>XYZ</MTART>
                        <POSTP>M</POSTP>
                    </YE1OROPR_MAT>
                </E1OROPR_MAT>
            </E1OROPR>
            <E1OROPR>
                <VORNR>0020</VORNR>
                <E1OROPR_MAT>
                    <MATNR>M0003-01</MATNR>
                    <YE1OROPR_MAT>
                        <STTXT>REL</STTXT>
                        <MTART>ZPAR</MTART>
                        <POSTP>L</POSTP>
                    </YE1OROPR_MAT>
                </E1OROPR_MAT>
            </E1OROPR>
        </E1ORHDR>
    </IDOC>
</YIORDER01>

And the output is something like this.

<PartOrderList>
   <PartOrder>
      <OperationBONumber>0010</OperationBONumber>
      <PartOrderLine>
         <MaterialNumber>M0003-01</MaterialNumber>
         <ShipmentType>REL</ShipmentType>
      </PartOrderLine>
   </PartOrder>
   <PartOrder>
      <OperationBONumber>0020</OperationBONumber>
   </PartOrder>
</PartOrderList>

And my XSLT is this

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="YIORDER01">
        <xsl:element name="PartOrderList">
            <xsl:for-each select="IDOC/E1ORHDR/E1OROPR">
                <xsl:element name="PartOrder">
                    <xsl:element name="OperationBONumber">
                        <xsl:value-of select="VORNR"/>
                    </xsl:element>
                    <xsl:for-each select="E1OROPR_MAT">
                        <xsl:if test="YE1OROPR_MAT/MTART = &apos;ZPAR&apos; and YE1OROPR_MAT/POSTP = &apos;L&apos;">
                            <xsl:element name="PartOrderLine">
                                <xsl:element name="MaterialNumber">
                                    <xsl:value-of select="MATNR"/>
                                </xsl:element>
                                <xsl:element name="ShipmentType">
                                    <xsl:value-of select="YE1OROPR_MAT/STTXT"/>
                                </xsl:element>
                            </xsl:element>
                        </xsl:if>
                    </xsl:for-each>
                </xsl:element>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

Problem here is, i don't want the second "PartOrder" tag in the output as it does not have any "PartOrderLine" child.

Rules used in the xslt:

  1. Create a "PartOrder" tag in output for each "E1OROPR" in input
  2. Create a "PartOrderLine" tag inside "PartOrder" only if "MTART" = "ZPAR" and "POSTP" = "L"
  3. Do not create any "PartOrder" tag if there is no valid "PartOrderLine" tag in the output.

Using my xslt I am able to achieve rules 1 and 2, but don't know how to achieve rule 3.
Is there any way I can achieve this using xslt??

Please help.

2
  • Side Note: I'd avoid using the for-each loop; XSLT is designed to work in a functional manner; using templates to do your for-each instead of an explicit for-each loop is therefore more in line with the language's intended use (giving you better performance and making things easier to maintain once you get your head around this approach. Commented Oct 29, 2013 at 14:10
  • NB: Your sample data also includes an additional r in the data which may be causing it to look like working code is buggy - <MTART>ZPAR r </MTART> Commented Oct 29, 2013 at 14:20

2 Answers 2

3

You need to add the conditions as a predicate to the outer for-each select expression so that you only for-each over elements that you know will generate at least one PartOrderLine

<xsl:for-each select="IDOC/E1ORHDR/E1OROPR[
      E1OROPR_MAT/YE1OROPR_MAT[MTART = 'ZPAR' and POSTP = 'L']]">
Sign up to request clarification or add additional context in comments.

Comments

0

FYI: here's how I'd approach it. The advantages of this method are:

  • Your valid line rule is only defined once, so should you change the logic it will change both for POs and POLines.
  • Every piece of data's easy to find should you wish to amend anything.
  • All templates can be run in parallel, (generally) making it faster on multi-processor machines.

.

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

  <xsl:output method="xml" indent="yes"/>

  <!-- Part Order List -->
  <xsl:template match="/*">
    <xsl:element name="PartOrderList">
      <xsl:apply-templates select=".//E1OROPR" /> 
    </xsl:element>
  </xsl:template>

  <!-- Part Order List > Part Order -->
  <xsl:template match="//E1OROPR">
    <xsl:variable name="IsValid">
      <xsl:call-template name="HasOrIsValidPOLine" />
    </xsl:variable>
    <xsl:if test="$IsValid='VALID'"> <!-- only display the part order if there's a valid line under it-->
      <xsl:element name="PartOrder">
        <xsl:apply-templates select=".//VORNR" />
        <xsl:apply-templates select=".//E1OROPR_MAT" />
      </xsl:element>
    </xsl:if>
  </xsl:template>

  <!-- Part Order List > Part Order > Operational BO Number -->
  <xsl:template match="//VORNR">
    <xsl:element name="OperationBONumber">
      <xsl:value-of select="text()"/>
    </xsl:element>
  </xsl:template>

  <!-- Part Order List > Part Order > Part Order Line -->
  <xsl:template match="//E1OROPR_MAT">
    <xsl:variable name="IsValid">
      <xsl:call-template name="HasOrIsValidPOLine" />
    </xsl:variable>
    <xsl:if test="$IsValid='VALID'">
      <!-- only display the part order line if it's valid-->
      <xsl:element name="PartOrderLine">
        <xsl:apply-templates select=".//MATNR" />
        <xsl:apply-templates select=".//STTXT" />
      </xsl:element>
    </xsl:if>
  </xsl:template>

  <!-- Part Order List > Part Order > Part Order Line > Material Number -->
  <xsl:template match="//MATNR">
    <xsl:element name="MaterialNumber">
      <xsl:value-of select="text()"/>
    </xsl:element>
  </xsl:template>

  <!-- Part Order List > Part Order > Part Order Line > Shipment Type -->
  <xsl:template match="//STTXT">
    <xsl:element name="ShipmentType">
      <xsl:value-of select="text()"/>
    </xsl:element>
  </xsl:template>

  <xsl:template name="HasOrIsValidPOLine">
    <xsl:choose>
      <xsl:when test=".//MTART/text() = 'ZPAR' and .//POSTP/text() = 'L'">VALID</xsl:when>
      <xsl:otherwise>INVALID</xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

That said there's probably a lot which could be done to improve this further / which option you choose should be down to the code you're most comfortable working with (to an extent; obviously any new approach has a learning curve which may cause initial discomfort, so this should be taken into account for the long term consideration).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.