This is the second time recently I came across this requirement so I guess others need it too. This post describes how to set a destintation node to null based upon a condition.
My situation was the source node contained a DateTime which was either valid or set to a zero length string. As some of you will know setting a date to a zero length string will actually set the date to 0001-01-01. That is the date when the world began, allegedly. My destination node is a date field too so I want to ignore the invalid date. But here's the thing - I need to set the target node to null because I need the node present in the output.
The solution is to use a logical operator (= or <>) and a value mapping functoid. In my case I am using a Script functoid to convert the source node value from DateTime to Date. So the Script output is a date string. By the way if you are wondering why I just don't test for this in the Script and set the return parameter to null, its becasue this doesn't work!
The <> functoid checks if the output is 0001-01-01 and sends true or false to the Value Mapping Functoid. As you can see I added 0001-01-01 as a constant for the second parameter. Remember this is a Not Equal operator so it will return true if its a valid date and false if it is 0001-01-01.
The magic happens in the Value Mapping functoid. The description explains what it does. If the value of the first input parameter is true then the value of the second input parameter is returned. It should add if the first input parameter is false, then the destination node is set to null.
So all you have to do is select the correct logical operator (either = or <>) so that its output is true if you want to pass the value to the destination and false when you want to set it to null. But be sure you have the parameters the right way round. The logical operator needs to be at the top, the value of the source node is below.
Update 24/04/2012: I also ran into a problem where the source node was a Record, that did not contain data but held other elements. I found that for the child elements the trick I used above worked well but, the parent element always appeared in the output as
< ParentNode />
I struggled for a long while to get rid of the offending < ParentNode /> but in the end found that simply using the Logical Existence (?) functoid on the ParentNode was enough. The result was the ParentNode was completely absent from the output except when any of the child elements contained data.
Showing posts with label Mapper. Show all posts
Showing posts with label Mapper. Show all posts
Tuesday, November 29, 2011
Monday, July 4, 2011
Transforming XML using BizTalk Mapper generated XSLT
If I have to write XSLT I try and make use of the BizTalk Mapper because it can generate XSLT in a fraction of the time. It also supplies a test harness so you can use an input file to test out your map.
You can add functoids to your map and I make use of the Scripting functoid so that I can write inline C# script. Here is an example of a function that takes a string source element and will truncate it if it is longer than the string length the target element can handle. It writes out an Information message to the event log if the maximum length is exceeded.
The function takes two extra parameters apart from the source element: the name of the source element and the maximum length. Add them by clicking on the ellipses next to Configure Functoid Inputs.
Here is the inline C# script.
public string Transform(string param1, string fieldname, int32 maxlength)
{
if (param1.Length > maxlength)
{
System.Diagnostics.EventLog.WriteEntry("Transformation", fieldname + " in excess of " + maxlength.ToString() + " chars, truncating", System.Diagnostics.EventLogEntryType.Warning);
return param1.Substring(0, 20);
}
else
{
return param1;
}
}
What BizTalk does is to write this out as a function at the botton of the XSLT. To produce the XSLT, use Validate Map and then navigate to where the output window has written the file.
Now I struggled a bit to get this XSLT to work until I finally found the answer was to use an XPathDocument instead of an XmlDocument. That's it. A transformation using the output from the BizTalk Mapper. Deep, deep joy.
Add the following using statements
using System.Xml;
using System.Xml.Xpath;
using System.Xml.Xslt;
// Transforms an XML document
// using an XSLT generated by BizTalk
// note use of XPathDocument
private XmlDocument Transform()
{
XslCompiledTransform xslt = new XslCompiledTransform();
// load the xslt
xslt.Load(@"C:\Projects\Import\MySchema.xsl", new XsltSettings(false, true), new XmlUrlResolver());
string filePathName = @"C:\Projects\Import\MyXML.xml";
//Load the XML data file.
XPathDocument doc1 = new XPathDocument(filePathName);
// create a memory stream
MemoryStream ms = new MemoryStream();
//Create an XmlTextWriter to write to the memory stream
XmlTextWriter writer = new XmlTextWriter(ms, Encoding.Unicode);
writer.Formatting = Formatting.Indented;
//Transform the file.
xslt.Transform(doc1, null, writer, null);
ms.Seek(0, SeekOrigin.Begin); // ** UPDATE changed from ms.Position=0 ****//
if (ms.Length == 0)
{
Exception ex = new Exception ("Transform error , output is null");
throw ex;
}
// load the memory stream into a XML document
XmlDocument output = new XmlDocument();
output.Load(ms);
writer.Close();
return output;
}
You can add functoids to your map and I make use of the Scripting functoid so that I can write inline C# script. Here is an example of a function that takes a string source element and will truncate it if it is longer than the string length the target element can handle. It writes out an Information message to the event log if the maximum length is exceeded.
The function takes two extra parameters apart from the source element: the name of the source element and the maximum length. Add them by clicking on the ellipses next to Configure Functoid Inputs.
Here is the inline C# script.
public string Transform(string param1, string fieldname, int32 maxlength)
{
if (param1.Length > maxlength)
{
System.Diagnostics.EventLog.WriteEntry("Transformation", fieldname + " in excess of " + maxlength.ToString() + " chars, truncating", System.Diagnostics.EventLogEntryType.Warning);
return param1.Substring(0, 20);
}
else
{
return param1;
}
}
What BizTalk does is to write this out as a function at the botton of the XSLT. To produce the XSLT, use Validate Map and then navigate to where the output window has written the file.
Now I struggled a bit to get this XSLT to work until I finally found the answer was to use an XPathDocument instead of an XmlDocument. That's it. A transformation using the output from the BizTalk Mapper. Deep, deep joy.
Add the following using statements
using System.Xml;
using System.Xml.Xpath;
using System.Xml.Xslt;
// Transforms an XML document
// using an XSLT generated by BizTalk
// note use of XPathDocument
private XmlDocument Transform()
{
XslCompiledTransform xslt = new XslCompiledTransform();
// load the xslt
xslt.Load(@"C:\Projects\Import\MySchema.xsl", new XsltSettings(false, true), new XmlUrlResolver());
string filePathName = @"C:\Projects\Import\MyXML.xml";
//Load the XML data file.
XPathDocument doc1 = new XPathDocument(filePathName);
// create a memory stream
MemoryStream ms = new MemoryStream();
//Create an XmlTextWriter to write to the memory stream
XmlTextWriter writer = new XmlTextWriter(ms, Encoding.Unicode);
writer.Formatting = Formatting.Indented;
//Transform the file.
xslt.Transform(doc1, null, writer, null);
ms.Seek(0, SeekOrigin.Begin); // ** UPDATE changed from ms.Position=0 ****//
if (ms.Length == 0)
{
Exception ex = new Exception ("Transform error , output is null");
throw ex;
}
// load the memory stream into a XML document
XmlDocument output = new XmlDocument();
output.Load(ms);
writer.Close();
return output;
}
Labels:
BizTalk Server,
Mapper,
Transform,
XML,
XSLT
Subscribe to:
Posts (Atom)


