How to use XSLT in AIF and what’s wrong with empty xml Nodes

The AIF framework is able to communicate with XML messages to the outer world. However there is a small limitation. The format of this XML message should sometimes be altered for this outside world. Microsoft BizTalk can be a solution, when messages are send to different parties and every party has his own XML layout, but when this message has to be send to just one party XSLT can help you out.
XSLT is a mechanism that can rebuild an XML message to a different format (XML to XML or XML to xHTML).
It is possible in AX to use XSLT (BASIC\SETUP\AIF\XSLT Repository. You can use these XSLT in the Endpoint Actions Pipeline Components. The XSLT in AX can only transform the body node of the XML message.
It is also possible to use directly the XSLT from code:
 XmlReader xslt = XmlReader::newFile(“Your file”);
 XmlReader xml = XmlReader::newFile(“Your file”);
 XmlDocument xmlDocument = xmlDocument::newXml(XmlTransform::execute(xslt,xml,false));

The best way to make an XSLT:
1                 create an XSD that represent the body of the AX XML message (Altova MapForce)
2                 get the XSD of the other party
3                 Generate the XSLT with a tool like Altova MapForce.
4                 Test the default AX XML and your XSLT in AX (form tutorial_XSL)
If you don’t have these tools you have to do it yourself. It is not so hard.  Let’s have a look at next example: (generated with AIF table ProjValProjCategorySetUp)
<?xml version="1.0" encoding="utf-16" ?>
<ProjValProjCategorySetUp xmlns="http://schemas.microsoft.com/dynamics/2006/02/documents/ProjValProjCategorySetUp">
    <DocPurpose>Original</DocPurpose>
    <SenderId>dmo</SenderId>
    <ProjValProjCategorySetUp class="entity">
      <CategoryId>Design</CategoryId>
     <GroupId>Design</GroupId>
    </ProjValProjCategorySetUp>
    <ProjValProjCategorySetUp class="entity">
    <CategoryId>Design-R&D</CategoryId>
      <ProjId>91</ProjId>
      <GroupId>Design</GroupId>
    </ProjValProjCategorySetUp>
    <ProjValProjCategorySetUp class="entity">
      <CategoryId>Install</CategoryId>
      <GroupId>Install</GroupId>
      <ProjId>92</ProjId>
  </ProjValProjCategorySetUp>
<ProjValProjCategorySetUp> 

Let’s use next XSLT on it. It should generate a document with looks like a table. The first node is the table name, his child node has the name record and the child nodes record have the nodes of the fields CategoryID and ProjId.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ax="http://schemas.microsoft.com/dynamics/2006/02/documents/ProjValProjCategorySetUp">

<xsl:template match = "ax:ProjValProjCategorySetUp">
    <ProjValProjCategorySetUp>
       <xsl:apply-templates select="ax:ProjValProjCategorySetUp[@class]"/>
  </ProjValProjCategorySetUp>
</xsl:template>

<xsl:template match = "ax:ProjValProjCategorySetUp[@class='entity']">
                    <record>
                    <CategoryID>
                                        <xsl:apply-templates  select="ax:CategoryId"/>
                    </CategoryID>
                    <ProjId>
                                        <xsl:apply-templates select="ax:ProjId"/>
                    </ProjId>
                    </record>
</xsl:template>
</xsl:stylesheet>

When processing this XSLT my XML will looks like:
<ProjValProjCategorySetUp xmlns:ax="http://schemas.microsoft.com/dynamics/2006/02/documents/ProjValProjCategorySetUp">
 <record>
    <CategoryId>Design</CategoryId>
    <ProjId></ProjId>
  </record>
 <record>
    <CategoryId>Design-R&D</CategoryId>
    <ProjId>91</ProjId>
  </record>
<record>
    <CategoryId>Install</CategoryId>
    <ProjId>92</ProjId>
  </record>
</ ProjValProjCategorySetUp> 

You are happy, you just generated your first XML transformation. But I have to disappoint you. A lot of tools that use your new XML document use XPATH technology. XPATH is some kind of query that can be used to select subsets out of your XML document. An XML query on your document could be:  Give all CategoryId were also a ProjId exist.
/ProjValProjCategorySetUp/record[ProjId]/CategoryId

The query fails because what is <ProjId></ProjID> is this equal to not exist or just a ProjID node with a value Empty?  To avoid these situations, AIF skips by default all empty nodes (Look at the original XML document from AIF. The first group has no ProjId node.)  If we also want to skip this empty node, we must adapt our XSLT document.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ax="http://schemas.microsoft.com/dynamics/2006/02/documents/ProjValProjCategorySetUp">
<xsl:template match = "ax:ProjValProjCategorySetUp">
                                        <ProjValProjCategorySetUp>
                                                            <xsl:apply-templates select="ax:ProjValProjCategorySetUp[@class]"/>
                                        </ProjValProjCategorySetUp>
                    </xsl:template>
                    <xsl:template match = "ax:ProjValProjCategorySetUp[@class='entity']">
                                        <record>
                                        <CategoryId>
                                                            <xsl:apply-templates  select="ax:CategoryId"/>
                                        </CategoryId>
                                        <xsl:if test="ax:ProjId">
                                           <ProjId>
                                                            <xsl:apply-templates select="ax:ProjId"/>
                                          </ProjId>
                                        </xsl:if>
                                        </record>
                    </xsl:template>
</xsl:stylesheet>

But personally I like next solution:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ax="http://schemas.microsoft.com/dynamics/2006/02/documents/ProjValProjCategorySetUp">
<xsl:template match = "ax:ProjValProjCategorySetUp">
                                        <ProjValProjCategorySetUp>
                                                            <xsl:apply-templates select="ax:ProjValProjCategorySetUp[@class]"/>
                                        </ProjValProjCategorySetUp>
                    </xsl:template>
                    <xsl:template match = "ax:ProjValProjCategorySetUp[@class='entity']">
                                        <record>
                                        <CategoryId>
                                                            <xsl:apply-templates  select="ax:CategoryId"/>
                                        </CategoryId>
                                        <ProjId>
                                                            <xsl:choose>
                                                            <xsl:when test="ax:ProjId">
                                                                                <xsl:apply-templates select="ax:ProjId"/>
                                                            </xsl:when>
                                                            <xsl:otherwise>
                                                                                BLANK
                                                            </xsl:otherwise>
                                                            </xsl:choose>
                                        </ProjId>
                                        </record>
                    </xsl:template>
</xsl:stylesheet> 


No comments:

Post a Comment

Note: Only a member of this blog may post a comment.