Logical Existence doesn't work - BizTalk Mapper - biztalk

This problem has confounded me for a while now. I have a flat file with segments and tag identifiers. One of the segments is optional. That is fine, but i need logic to determine if this segment exists. If it does not exist i need to do something else. All works as planned if the segment exists. If it doesn't, it appears BizTalk does not even recognize or execute any functoids related to the segment.
Here is the flat file segment:
And here is my Logical Existence connected to a Logical NOT:
Again, if the segment is completely gone (meaning no flat file xml nodes are created/translated), the Logical Existence does not even execute. I've tried scripts, functoids. I'm becoming convinced this is a bug in the mapper. Any help appreciated.

Try using the Value Mapping functoid instead of the Value Mapping (Flattening) functoid.
Also ensure that the parameters of the Value Mapping functoids are in the correct order. The logical operator must be the first parameter. Sometimes the parameters go out of order and the functoid stops working as expected.

Since your input file is a flat file, the FFDASM is creating an empty node. The test run by Logical Existence evaluates to true on an empty node. Logical String should work here (I was previously thinking it would return true for an empty string but it shouldn't).
However, in this case, I'd probably replace all of that (including the value mapper) with a C# scripting functoid:
public string AllowIfNotEmpty(string test, string output)
{
if (!string.IsNullOrWhiteSpace(test))
return output;
return "";
}
Give it the input of the node currently linked to Logical String first, and the second input to your Value Mapping(flattening) second, and output it directly to your destination node.
You could put that into a helper assembly if it's something you use in multiple places.
If you wanted to keep it as XSLT, you could do a custom call template:
<xsl:template name="OutputIfNotEmpty">
<xsl:param name="test" />
<xsl:param name="output" />
<xsl:if test="normalize-space($test) != ''">
<xsl:element name="OutputElementName">
<xsl:value-of select="$output" />
</xsl:element>
</xsl:if>
</xsl:template>

Related

Biztalk Map, How to execute a scripting functoid ONLY IF the source message contains a specific value?

In a Biztalk map, how can I execute a scripting functoid ONLY IF the source message contains a specific value? Which functoid combination can allow me to do this?
Edit
I want to check a certain element for a specific value.
The scripting will be inline xslt.
In response to 40Alpha first suggestion: This is the method I would like to use but I can't connect the scripting functoid to the value mapping functoid. I'm probably doing something wrong here?
The two easiest options (IMO) would be:
What I would do is:
Create an Equal Functoid (1st condition being that "certain element" and 2nd condition being the text you would like to check it against)
Create a Value Mapping Functoid and connect the Equal Functoid (that you just made) to it first and then connect it your scripting functoid.
Now connect your scripting functoid to the desired target element(s).
OR a simpler approach would be:
Wrap your xslt with this in your scripting functoid:
<xsl:if test="/yournode[text()="SomeValue"]">
<!--YOUR SCRIPTING HERE -->
</xsl:if>

BizTalk HIPAA EDI multi-input map doesn't generate node

I had asked this question 2 years ago (Problem with BizTalk multi-input map), but then the project was shelved and I never did any further testing. I'm having to dust it off again, with some more details and screenshots.
I have a BizTalk HIPAA solution that needs to merge an 837 claim schema with some data from our system. We're doing this with a map that has two input schemas, as shown here:
(I've greatly simplified the schemas for testing purposes).
The accepted answer to my original post (using an equal functoid and value mappers) works fine with a simple schema like I had originally shown, but fails with the actual EDI schema.
In the first input message, if IsRepriced = 1, I want to use our values for HCP_01, HCP_02, and HCP_03. If it's 0, use the values in the second message (the original 837 claim). The functoids shown work fine as long as the original 837 claim actually contains the HCP node (segment), but if it's not there I'm unable to generate one from our data.
Replacing these with a scripting functoid using some if-then-else C# code has the same effect.
So, is there a way to do this using functoids, or do I need to resort to XSLT? Unfortunately I know next to nothing about XSLT, so that's going to be difficult...
Thanks!
Edit: I would up using an Inline XSLT Call Template, with this code:
<xsl:template name="Repricing_2000B_HCP">
<xsl:param name="IsRepriced" />
<xsl:choose>
<xsl:when test="$IsRepriced='1'">
<xsl:for-each select="//InputMessagePart_1/ns0:X12_00401_837_I/ns0:TS837Q3_2000A_Loop/ns0:TS837Q3_2000B_Loop/ns0:TS837Q3_2300_Loop/ns0:HCP_ClaimPricingRepricingInformation_TS837Q3_2300">
<xsl:element name="ns0:HCP_ClaimPricingRepricingInformation_TS837Q3_2300">
<xsl:copy-of select="./#*" />
<xsl:copy-of select="./*" />
</xsl:element>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="//InputMessagePart_0/ns0:X12_00401_837_I/ns0:TS837Q3_2000A_Loop/ns0:TS837Q3_2000B_Loop/ns0:TS837Q3_2300_Loop/ns0:HCP_ClaimPricingRepricingInformation_TS837Q3_2300">
<xsl:element name="ns0:HCP_ClaimPricingRepricingInformation_TS837Q3_2300">
<xsl:copy-of select="./#*" />
<xsl:copy-of select="./*" />
</xsl:element>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I frequently hit a brick wall with with the if missing-else paradign in a Map. Sometimes Looping Functioids do the trick, sometimes not. Sometimes a carefully placed Logical Existence -> Not works, sometimes no.
It's mostly because if one of the source Records is minOccurs=0, the Mapper will wrap everything in a for-each. Since the Element doesn't exist, the code never gets executed.
I'd say >50%, I resort to inline Xslt because at some point, it becomes cleaner than a page of mostly duplicate Functoid groups.
However, I'd bet the Xslt won't turn out as difficult a proposition as you think. The Mapper can do most of the work for you. You can build the bulk of the Map using Functoids, then just copy the resulting Xslt. You will have to modify for some things, like namespaces.
(Same answer)

How to select xml elements within a namespace?

I'm trying to understand this xslt.
What does the below xslt command select exactly? what are "following-sibling", "aic" and "pstyle"?
"aic" seems to be a namespace.
What xml input the below xslt work with?
<xsl:stylesheet exclude-result-prefixes="aic"
version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:aic="http://ns.adobe.com/AdobeInCopy/2.0/" >
<xsl:template match="/">
<xsl:value-of select="following-sibling::aic:pstyle"/>
</xsl:template>
</xsl:stylesheet>
following-sibling::aic:pstyle
following-sibling is the axis, denoting which "direction" to look for nodes, in this case it looks at nodes which are after the current context node in document order but share the same parent as the current node. If you don't specify an axis the default is child, which looks for child nodes of the current context node.
aic:pstyle is a selector that looks for elements whose local name is pstyle and whose namespace URI is http://ns.adobe.com/AdobeInCopy/2.0/ (the one that is mapped to the prefix aic in the stylesheet).
The source XML need not use the same prefix, e.g. the expression would match an element that looks like
<pstyle xmlns="http://ns.adobe.com/AdobeInCopy/2.0/">
or
<foo:pstyle xmlns:foo="http://ns.adobe.com/AdobeInCopy/2.0/">
in the original XML.
As JLRishe points out, this particular XPath will not match anything if the current context is the document node /, for the expression to be meaningful it would have to be executed in a context where the current node is an element (or comment, processing instruction or text node) at least two levels down i.e. a child of the document element or deeper.
<example xmlns="http://ns.adobe.com/AdobeInCopy/2.0/">
<pstyle id="1"/>
<foo/>
<pstyle id="2"/>
<pstyle id="3"/>
</example>
If executed with the foo element as the context node, the expression would select pstyle elements 2 and 3, but not 1.

Access the HTTP Response from xdmp:http-get()

Using MarkLogic to pull in data from a web service with xdmp:http-get() or xdmp:http-post(), I'd like to be able to check the headers that come back before I attempt to process the data. In DQ I can do this:
let $result := xdmp:http-get($query,$options) (: $query and $options are fine, I promise. :)
return $result
And the result I get back looks like this:
<v:results v:warning="more than one node">
<response>
<code>200</code>
<message>OK</message>
<headers>
<server>(actual server data was here)</server>
<date>Thu, 07 Jun 2012 16:53:24 GMT</date>
<content-type>application/xml;charset=UTF-8</content-type>
<content-length>2296</content-length>
<connection>close</connection>
</headers>
</response>
followed by the actual response. the problem is that I can't seem to XPath into this response node. If I change my return statement to return $result/response/code I get the empty sequence. If I could check that code to make sure I got a 200 back before attempting to process the actual data that came back it would be much better than using try-catch blocks to see if the data exists and is sane.
So, if anyone knows how to access those response codes I would love to see your solution.
For the record, I have tried xdmp:get-response-code(), but it doesn't take any parameters, so I don't don't know what response code it's looking at.
You're getting burned by two gotchas at once:
awareness of namespaces
awareness of document nodes
First, the namespace. The XML output of the http-get function is in a namespace as seen by the top-level element:
<response xmlns="xdmp:http-get">
To successfully access elements in that namespace, you need to declare a prefix in your query bound to the correct namespace, and then use that prefix in your XPath expressions. For example:
declare namespace h="xdmp:http-get";
//h:code
Now lets talk about document nodes. :-)
You're trying to access $result as if it is a document node containing an element, but in actuality, it is a sequence of two root nodes (so they're not siblings either). The first one (the one you're interested in here) is a parentless <response> element—not a document containing a <response> element.
This is a common gotcha: knowing when a document node is present or not. Document nodes are always invisible when serialized (hence the gotcha), and they're always present on documents stored in the database. However, when you just use a bare element constructor in XQuery (as the http-get implementation does), you construct not a document node but an element node without a document node parent.
For example, the following query will return the empty sequence, because it's trying to get the <foo> child of <foo>:
declare variable $foo := <foo>bar</foo>;
$foo/foo
On the other hand, the following does return <foo>, because it's getting the <foo> child of the document node (which has to be explicitly constructed, in XQuery):
$declare variable $doc := document{ <foo>bar</foo> };
$doc/foo
So you have to know how a given function's API is designed (whether it returns a document containing an element or just an element).
To solve your problem, don't try to access $result/h:response/h:code (which is trying to get the <response> child of <response>). Instead, access $result/h:code (or more precisely $result[1]/h:code, since <response> is the first of a sequence of two nodes returned by the http-get function).
For more information on document nodes, check out this blog article series: http://community.marklogic.com/blog/document-formats-part1

How to pass "Null" (a real surname!) to a SOAP web service in ActionScript 3

We have an employee whose surname is Null. Our employee lookup application is killed when that last name is used as the search term (which happens to be quite often now). The error received (thanks Fiddler!) is:
<soapenv:Fault>
<faultcode>soapenv:Server.userException</faultcode>
<faultstring>coldfusion.xml.rpc.CFCInvocationException: [coldfusion.runtime.MissingArgumentException : The SEARCHSTRING parameter to the getFacultyNames function is required but was not passed in.]</faultstring>
Cute, huh?
The parameter type is string.
I am using:
WSDL (SOAP)
Flex 3.5
ActionScript 3
ColdFusion 8
Note that the error does not occur when calling the webservice as an object from a ColdFusion page.
Tracking it down
At first I thought this was a coercion bug where null was getting coerced to "null" and a test of "null" == null was passing. It's not. I was close, but so very, very wrong. Sorry about that!
I've since done lots of fiddling on wonderfl.net and tracing through the code in mx.rpc.xml.*. At line 1795 of XMLEncoder (in the 3.5 source), in setValue, all of the XMLEncoding boils down to
currentChild.appendChild(xmlSpecialCharsFilter(Object(value)));
which is essentially the same as:
currentChild.appendChild("null");
This code, according to my original fiddle, returns an empty XML element. But why?
Cause
According to commenter Justin Mclean on bug report FLEX-33664, the following is the culprit (see last two tests in my fiddle which verify this):
var thisIsNotNull:XML = <root>null</root>;
if(thisIsNotNull == null){
// always branches here, as (thisIsNotNull == null) strangely returns true
// despite the fact that thisIsNotNull is a valid instance of type XML
}
When currentChild.appendChild is passed the string "null", it first converts it to a root XML element with text null, and then tests that element against the null literal. This is a weak equality test, so either the XML containing null is coerced to the null type, or the null type is coerced to a root xml element containing the string "null", and the test passes where it arguably should fail. One fix might be to always use strict equality tests when checking XML (or anything, really) for "nullness."
Solution
The only reasonable workaround I can think of, short of fixing this bug in every damn version of ActionScript, is to test fields for "null" and escape them as CDATA values.
CDATA values are the most appropriate way to mutate an entire text value that would otherwise cause encoding/decoding problems. Hex encoding, for instance, is meant for individual characters. CDATA values are preferred when you're escaping the entire text of an element. The biggest reason for this is that it maintains human readability.
On the xkcd note, the Bobby Tables website has good advice for avoiding the improper interpretation of user data (in this case, the string "Null") in SQL queries in various languages, including ColdFusion.
It is not clear from the question that this is the source of the problem, and given the solution noted in a comment to the first answer (embedding the parameters in a structure) it seems likely that it was something else.
The problem could be in Flex's SOAP encoder. Try extending the SOAP encoder in your Flex application and debug the program to see how the null value is handled.
My guess is, it's passed as NaN (Not a Number). This will mess up the SOAP message unmarshalling process sometime (most notably in the JBoss 5 server...). I remember extending the SOAP encoder and performing an explicit check on how NaN is handled.
#doc_180 had the right concept, except he is focused on numbers, whereas the original poster had issues with strings.
The solution is to change the mx.rpc.xml.XMLEncoder file. This is line 121:
if (content != null)
result += content;
(I looked at Flex 4.5.1 SDK; line numbers may differ in other versions.)
Basically, the validation fails because 'content is null' and therefore your argument is not added to the outgoing SOAP Packet; thus causing the missing parameter error.
You have to extend this class to remove the validation. Then there is a big snowball up the chain, modifying SOAPEncoder to use your modified XMLEncoder, and then modifying Operation to use your modified SOAPEncoder, and then moidfying WebService to use your alternate Operation class.
I spent a few hours on it, but I need to move on. It'll probably take a day or two.
You may be able to just fix the XMLEncoder line and do some monkey patching to use your own class.
I'll also add that if you switch to using RemoteObject/AMF with ColdFusion, the null is passed without problems.
11/16/2013 update:
I have one more recent addition to my last comment about RemoteObject/AMF. If you are using ColdFusion 10; then properties with a null value on an object are removed from the server-side object. So, you have to check for the properties existence before accessing it or you will get a runtime error.
Check like this:
<cfif (structKeyExists(arguments.myObject,'propertyName')>
<!--- no property code --->
<cfelse>
<!--- handle property normally --->
</cfif>
This is a change in behavior from ColdFusion 9; where the null properties would turn into empty strings.
Edit 12/6/2013
Since there was a question about how nulls are treated, here is a quick sample application to demonstrate how a string "null" will relate to the reserved word null.
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" initialize="application1_initializeHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected function application1_initializeHandler(event:FlexEvent):void
{
var s :String = "null";
if(s != null){
trace('null string is not equal to null reserved word using the != condition');
} else {
trace('null string is equal to null reserved word using the != condition');
}
if(s == null){
trace('null string is equal to null reserved word using the == condition');
} else {
trace('null string is not equal to null reserved word using the == condition');
}
if(s === null){
trace('null string is equal to null reserved word using the === condition');
} else {
trace('null string is not equal to null reserved word using the === condition');
}
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
</s:Application>
The trace output is:
null string is not equal to null reserved word using the != condition
null string is not equal to null reserved word using the == condition
null string is not equal to null reserved word using the === condition
Translate all characters into their hex-entity equivalents. In this case, Null would be converted into &#4E;K&#6C;&#6C;
Stringifying a null value in ActionScript will give the string "NULL". My suspicion is that someone has decided that it is, therefore, a good idea to decode the string "NULL" as null, causing the breakage you see here -- probably because they were passing in null objects and getting strings in the database, when they didn't want that (so be sure to check for that kind of bug, too).
As a hack, you could consider having a special handling on the client side, converting 'Null' string to something that will never occur, for example, XXNULLXX and converting back on the server.
It is not pretty, but it may solve the issue for such a boundary case.
Well, I guess that Flex' implementation of the SOAP Encoder seems to serialize null values incorrectly. Serializing them as a String Null doesn't seem to be a good solution. The formally correct version seems to be to pass a null value as:
<childtag2 xsi:nil="true" />
So the value of "Null" would be nothing else than a valid string, which is exactly what you are looking for.
I guess getting this fixed in Apache Flex shouldn't be that hard to get done. I would recommend opening a Jira issue or to contact the guys of the apache-flex mailinglist. However this would only fix the client side. I can't say if ColdFusion will be able to work with null values encoded this way.
See also Radu Cotescu's blog post How to send null values in soapUI requests.
It's a kludge, but assuming there's a minimum length for SEARCHSTRING, for example 2 characters, substring the SEARCHSTRING parameter at the second character and pass it as two parameters instead: SEARCHSTRING1 ("Nu") and SEARCHSTRING2 ("ll"). Concatenate them back together when executing the query to the database.

Resources