How to copy the entire node to element of string type in a map

Posted: August 1, 2009  |  Categories: BizTalk Uncategorized

 

I wanted to copy the entire Contact node in the map below to the ContactDetails element of type string.

XML2Text

I could have done this in an orchestration by assigning the node to an XmlDocument variable and then assigning the XmlDocument.InnerText to a distinguished field in the outbound message but I wanted to do this in map  because I plan to put the map on the outbound port and not in an orchestration.

My  first try almost worked. I used a scripting functoid with this Inline XSLT.

<!–MassCopy alternative that does not copy the namespace.–>

<ns1:ContactDetails

<xsl:copy-of select=”*[local-name()=’Contact’]”/>

</ns1:ContactDetails>

This almost works but I get this error on validation;

The element ‘http://schemas.microsoft.com/Sql/2008/05/TypedProcedures/dbo:ContactDetails’ cannot contain child element ‘http://Blah.ContactDetails:Contact’ because the parent element’s content model is text only.

The problem here is that it’s copying the node structure to the outbound message and not mapping the node structure to a string.

So i have tried two solutions from posts by Greg Forsythe namely http://www.biztalkgurus.com/forums/t/12350.aspx and http://connectedpawns.wordpress.com/2008/10/08/call-a-net-assembly-from-custom-xslt/ ( I had to use my blog because the original link seems to be broken). I have decided to record how I did it because Greg’s instructions are very brief and it took me a while to work out what to do.

The first step is to create a helper class and deploy it to the GAC .

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.XPath;

namespace CopyXML2TextNodeBTSDemo.Utility
{
    public class Utilities
    {
        public static string ConvertNodeToXmlString(XPathNodeIterator node)

        {      nodes.MoveNext();

               return node.Current.OuterXml;

         }

    }
}

Secondly reference this assembly in the BizTalk map project.

Add a scripting functoid to the map , choose external assembly as the scripting type with the values shown below and connect it to the a field in the outbound schema.

scriptingfunctoid

Validate the map to generate a xsl file and a Custom extension XML file. The output is shown below

Invoking component.….CopyXML2TextNodeBTSDemo.Maps\SubmitContactDetails2XMLdatafieldasTEXT.btm: The compilation is using the CustomXslt and CustomExtensionXml tags to generate the output.  The map content is ignored.
….CopyXML2TextNodeBTSDemo.Maps\SubmitContactDetails2XMLdatafieldasTEXT.btm: The output XSLT is stored in the following file: <file:///C:\..\_MapData\SubmitContactDetails2XMLdatafieldasTEXT.xsl> 
  ….\SubmitContactDetails2XMLdatafieldasTEXT.btm: The Extension Object XML is stored in the following file: <file:///C:\..\_MapData\SubmitContactDetails2XMLdatafieldasTEXT_extxml.xml>
Component invocation succeeded.

Save both files and add them to the BizTalk map project.

Change the map to use a the generated xsl file as the custom XSLT and the Extension Object XML file as shown below.

customXSLT

Edit xsl file like so;

<?xml version=”1.0″ encoding=”UTF-16″?>
<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” xmlns:msxsl=”urn:schemas-microsoft-com:xslt” xmlns:var=”http://schemas.microsoft.com/BizTalk/2003/var” exclude-result-prefixes=”msxsl var s0 ScriptNS0″ version=”1.0″ xmlns:ns0=”http://CopyXML2TextNodeBTSDemo.XMLdatafieldasTEXT” xmlns:s0=”http://CopyXML2TextNodeBTSDemo.SubmitContactDetails” xmlns:ScriptNS0=”http://schemas.microsoft.com/BizTalk/2003/ScriptNS0″>
    <xsl:output omit-xml-declaration=”yes” method=”xml” version=”1.0″ />
    <xsl:template match=”/”>
        <xsl:apply-templates select=”/s0:SubmitContactDetails” />
    </xsl:template>
    <xsl:template match=”/s0:SubmitContactDetails”>
        <ns0:ContainsXMLfieldasText>
            <ContactId>
                <xsl:value-of select=”ContactId/text()” />
            </ContactId>
            <xsl:apply-templates  select=”Contact”/>
        </ns0:ContainsXMLfieldasText>
    </xsl:template>
    <xsl:template  match=”Contact”>
        <xsl:variable name=”var:v1″ select=”ScriptNS0:ConvertNodeToXmlString(.)” />
        <ContactDetails>
            <xsl:value-of select=”$var:v1″ />
        </ContactDetails>
    </xsl:template>
</xsl:stylesheet>

Now if you test the map you get the desired output. Whew!!

—————————————————————————– Added 27/2/2011

Another way to do this is use an inline XSLT template in a map something like this

<!–Uncomment the following xslt for a sample Xslt Call Template that creates a Field element whose value is the concatenatation of the two inputs. Change the number of parameters of this template to be equal to the number of inputs connected to this functoid.–>

<xsl:template name=”called-template”>
  <xsl:param name=”param1″ />
  <xsl:element name=”ns0:Message”>
    <xsl:text disable-output-escaping=”yes”>&lt;![CDATA[</xsl:text>
    <xsl:call-template name=”identity” />
    <xsl:text disable-output-escaping=”yes”>]]&gt;</xsl:text>
  </xsl:element>
</xsl:template>
<xsl:template name=”identity” match=”@*|node()”>
  <xsl:copy>
    <xsl:apply-templates select=”@*|node()” />
  </xsl:copy>
</xsl:template>

  • Chandu

    Bravo sir… you made my day!

  • Chandu

    Hi,
    A quick quesiton on your sample, do you ignore the below error when validating the map to capture the xsl and xml file?

    error btm1010: The “Scripting” functoid has 0 input parameter(s), but 1 parameter(s) are expected.

    • mbrimble

      Yes all you want is the xsl.

  • HelderSoares

    Hi,

    Stephen’s jottings found a simple way to do this! Anyway many thks for your post!

    http://stephenomo.blogspot.com/2011/11/convert-xml-file-to-string-inside.html

    • mbrimble

      Added this as a change to my post on 27/2/2011. Did you read this?

  • this comparison

    Hey there just wanted to give you a quick heads up and let you
    know a few of the pictures aren’t loading correctly. I’m not sure
    why but I think its a linking issue. I’ve tried it in two different browsers and both show the same outcome.

    • mbrimble

      I hae just checked the pictures and they seen Ok to me.

  • Paul Nichols

    Hi

    I can’t thank you enough for this article.
    I’m not saying my final solution is better but I thought I’d mention it because it seemed simpler 
    Rather than adding a method to an assembly and having to GAC it (which I know gives you reusability and unit testability) I just placed a scripting functiod on my map containing the following c# –

    public string ToXmlString(XPathNodeIterator node)
    {
    return node.Current.OuterXml;
    }

    The c# functiod did not have an output (no line to a field in the output schema)

    I then added another scripting functiod to the map. This time I linked it to my output/destination node/element and set it to be an inline XSLT (not call template). I added this XSL to the functiod (BizTalkProcessResult being my target node) –

    I was pleased to see these two functiods solved my problems and meant that I didn’t have to resort to custom XSLTs therefore losing the benefits of the map designer. I had problems with XSLT call templates such as the following

    <![CDATA[

    ]]>

    Doing it this XSLT way seemed to result in losing namespace declarations on attributes, plus it seems more complicated to me. I just needed an exact copy of the source message as a string

    thanks again

    Paul

  • Paul Nichols

    Hi

    I can’t thank you enough for this article.
    I’m not saying my final solution is better but I thought I’d mention it because it seemed simpler 
    Rather than adding a method to an assembly and having to GAC it (which I know gives you reusability and unit testability) I just placed a scripting functiod on my map containing the following c# –

    public string ToXmlString(XPathNodeIterator node)
    {
    return node.Current.OuterXml;
    }

    The c# functiod did not have an output (no line to a field in the output schema)

    I then added another scripting functiod to the map. This time I linked it to my output/destination node/element and set it to be an inline XSLT (not call template). I added this XSL to the functiod (BizTalkProcessResult being my target node) –

    <![CDATA[<

    ]]>

    I was pleased to see these two functiods solved my problems and meant that I didn’t have to resort to custom XSLTs therefore losing the benefits of the map designer. I had problems with XSLT call templates such as the following

    <![CDATA[

    <![CDATA[

    ]]>

    ]]>

    Doing it this XSLT way seemed to result in losing namespace declarations on attributes, plus it seems more complicated to me. I just needed an exact copy of the source message as a string

    thanks again

    Paul

    PS the CDATA tags above in my code examples where just so i could post this reply otherwise the xml was removed from the post

  • Paul Nichols

    Hi

    I can’t thank you enough for this article.
    I’m not saying my final solution is better but I thought I’d mention it because it seemed simpler 
    Rather than adding a method to an assembly and having to GAC it (which I know gives you reusability and unit testability) I just placed a scripting functiod on my map containing the following c# –

    public string ToXmlString(XPathNodeIterator node)
    {
    return node.Current.OuterXml;
    }

    The c# functiod did not have an output (no line to a field in the output schema)

    I then added another scripting functiod to the map. This time I linked it to my output/destination node/element and set it to be an inline XSLT (not call template). I added this XSL to the functiod (BizTalkProcessResult being my target node) –

    <!–Uncomment the following

    –>

    I was pleased to see these two functiods solved my problems and meant that I didn’t have to resort to custom XSLTs therefore losing the benefits of the map designer. I had problems with XSLT call templates such as the following

    <![CDATA[

    ]]>

    –>

    Doing it this XSLT way seemed to result in losing namespace declarations on attributes, plus it seems more complicated to me. I just needed an exact copy of the source message as a string

    thanks again
    Paul

  • Paul Nichols

    i was smart enough to solve this problem but not smart enough to post my solution without losing the xml sections of it!

One Platform Operations, Monitoring and Analytics Software
BizTalk360

microsoft biztalk

Learn more

Over 500 customers across 30+ countries depend on BizTalk360

ServiceBus360

Azure service bus

Learn more

Start managing your Azure Service Bus namespaces in minutes

One Platform - Operations, Monitoring and Analytics Software
BizTalk360

microsoft biztalk

Learn more

Over 500 customers across 30+ countries depend on BizTalk360

One Platform - Operations, Monitoring and Analytics Software
ServiceBus360

Azure service bus

Learn more

Start managing your Azure Service Bus namespaces in minutes

Back to Top