BizTalk Mapper: Working With Nillable Values (xsi:nil="true")

Posted: June 12, 2014 in BizTalk
Tags: , , , , , , , , , , ,

Basically there are two properties inside the schema element decides whether an element can be absent from the document: Min Occurs and Nillable.

If Min Occurs property is set 0 then that element can be absent from the XML message but if it is set to 1 it has to be present though its value can be empty. This is useful to reduce the size of the document if only not all the elements are mandatory to end systems.

In other hand, if the Nillable property of the element is set to true, this will indicate that the value of an element in the document may be null. This NULL values will be expressed with xsi:nil = true attribute in the element, ex:

<IntExist xsi:nil="true" />

An element with the attribute xsi:nil = true explicitly means that the value is unavailable or unknown at that moment and sometimes the end system explicitly requires to be notified that the value of the element is NULL so that they can take appropriate action.

In this sample scenario we will have a 2 mandatory elements that can be nillable that we need to map to the destination schema. In this scenario all the destination elements are also mandatory and we need to fill them with a valid value or specify the nillable property as true: so if the element exist we need to map the correct source value otherwise we need to set the destination element as nillable.

The first element “DateExist” is a mandatory element that can be null. If null we need to set a null value in the destination element also as null, otherwise we need to map the source value. To accomplish that we need to:

  • Drag one IsNil Functoid from the Toolbox window onto the Grid.
    • Drag a link from the “NillValue” field element in the source schema to the IsNill Functoid
  • Drag one Nil Value Functoid from the Toolbox window onto the Grid.
  • Drag one Logical NOT Functoid from the Toolbox window onto the Grid.
  • Drag one Value Mapping Functoid from the Toolbox window onto the Grid.
  • To create a rule for mapping the value if the element is null
    • Drag a link from the IsNill Functoid to the Nil Value Functoid
    • Drag a link from the Nil Value Functoid to the “NillValueOutput” field element in the destination schema
  • Otherwise, To create a rule for mapping the value if the element different of null
    • Drag a link from the IsNill Functoid to the Logical NOT Functoid
    • Drag a link from the Logical NOT Functoid to the Value Mapping Functoid Functoid
    • Drag a link from the “NillValue” field element in the source schema to the Value Mapping Functoid
    • Drag a link from the Value Mapping Functoid to the “NillValueOutput” field element in the destination schema

Do the exact same logic for the second element present in the source schema.

BizTalk-Mapper-Working-With-Nillable-Values

Sometimes the maps are misunderstood and notorious for producing a lot of unnecessary code that may cause a in some cases lack of performance. So the question that we can and should ask is whether this is the best solution or not to address this type of operations. To respond this question we should also inspect the generated code produce by the BizTalk Mapper:

<xsl:variable name="var:v1" select="string(NillValue/@xsi:nil) = 'true'" />
    <xsl:variable name="var:v2" select="userCSharp:LogicalNot(string($var:v1))" />
    <xsl:variable name="var:v4" select="string(AnotherNilValue/@xsi:nil) = 'true'" />
    <xsl:variable name="var:v5" select="userCSharp:LogicalNot(string($var:v4))" />
    <ns0:OutputSchema>
      <xsl:if test="string($var:v1)='true'">
        <NillValueOutput>
          <xsl:attribute name="xsi:nil">
            <xsl:value-of select="'true'" />
          </xsl:attribute>
        </NillValueOutput>
      </xsl:if>
      <xsl:if test="string($var:v2)='true'">
        <xsl:variable name="var:v3" select="NillValue/text()" />
        <NillValueOutput>
          <xsl:value-of select="$var:v3" />
        </NillValueOutput>
      </xsl:if>
      <xsl:if test="string($var:v4)='true'">
        <AnotherNilValueOutput>
          <xsl:attribute name="xsi:nil">
            <xsl:value-of select="'true'" />
          </xsl:attribute>
        </AnotherNilValueOutput>
      </xsl:if>
      <xsl:if test="string($var:v5)='true'">
        <xsl:variable name="var:v6" select="AnotherNilValue/text()" />
        <AnotherNilValueOutput>
          <xsl:value-of select="$var:v6" />
        </AnotherNilValueOutput>
      </xsl:if>
    </ns0:OutputSchema>
  </xsl:template>

In fact is a pretty decent XSLT code but the reality is that it can be better, we don’t need to use any support variables and we can remove one if condition by replacing the xsl:if condition for one xsl:choose condition.

This is a very simple approach, easy to implement and readable that you should use even in small or large messages (transformations) but only if you have to deal with a small number of nillable element.

However applying this approach in transformation that will need to deal with a large number of nillable elements, can lead to two problems:

  • A lot of unnecessary XSLT code that can in fact and of course always depending in the size of the message can lead to some lack of performance
  • A lot of functoid shapes (4 Functoids) and links (7 links) for each element that can lead to lack of visual Readability

So can we improve this solution for transformations that needs to deal with a large number of nillable elements?

Well that’s the problem, there isn’t a simple solution for that. At the first look you may think that’s easy, just copy the XSLT code inside to a Scripting Functoid and optimize the XSLT code.

However by doing that you will receive an error:

error btm1050: XSL transform error: Unable to write output instance to the following <file:///C:\…\MapNillValuesWithCustomXSLT_output.xml>. Prefix ‘xsi’ is not defined.

The problem is that the nil attribute is defined in the XML Schema instance namespace, http://www.w3.org/2001/XMLSchema-instance (commonly associated with the prefix xsi) and this namespace is not declared by default in the XSL code generated by the BizTalk Mapper.

This namespace is automatically declare only if you use the Nil Functoids in the map.

So the normal solution here is… to bypass the BizTalk Mapper and generate an external XSLT code and add it to the map by specifying the Custom XSLT Path by:

  • Open the map
  • Click the grid zone and on the properties window there will be a “Custom XSLT Path” property.  Click the ellipses and navigate to the file containing the XSLT.

BizTalk-Mapper-Working-With-Nillable-Values-External-XSLT

You then can use a similar code to check and map the elements:

<xsl:choose>
  <xsl:when test="NillValue/@xsi:nil">
    <NillValueOutput>
      <xsl:attribute name="xsi:nil">
        <xsl:value-of select="'true'" />
      </xsl:attribute>
    </NillValueOutput>
  </xsl:when>
  <xsl:otherwise>
    <NillValueOutput>
      <xsl:value-of select="NillValue/text()" />
    </NillValueOutput>
  </xsl:otherwise>
</xsl:choose>

However applying this approach we have a major problem for me:

  • We lose all the BizTalk Mapper functionalities.
Workaround

Well, at least that I know, unfortunately there is no simple way to declared the xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance namespace to the stylesheet of the map.

However we can apply one small workaround, is not perfect but in most of the case it will solve my problems:

  • For only one of the nillable elements we need to use the Nil Functoids explained in the beginning of this post.
    • This will declare automatically the name xsi namespace for us.
  • In the rest of the elements we now can use Scripting Functoids with the optimized XSLT code described above

BizTalk-Mapper-Working-With-Nillable-Values-XSLT

I’m still working to find a better way but until then

You can download the source code from:

BizTalk Mapper: Working With Nillable Values (xsi:nil=”true”) (92.7 KB)
Microsoft | Code Gallery

Comments
  1. Add the namespace to the attribute

  2. xsl:attribute name=”xsi:nil” namespace=”xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance”

    • Hi Fernando,
      I try that solution but when I test the map it will generate an error btm1050: XSL transform error: Unable to write output instance to the following… Prefix ‘xsi’ is not defined.

  3. If you own/control either the source or the target schema then just add the namespace there. it will then be added to the map in the imports section.

  4. sorry the declaration must be namespace=”http://www.w3.org/2001/XMLSchema-instance” not namespace=”xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s