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

Asked 2023-09-20 20:05:27 View 116,869

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 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>

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.

Answers

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.

Answered   2023-09-20 20:05:27

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.

Answered   2023-09-20 20:05:27

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.

Answered   2023-09-20 20:05:27

  • name="Null" is of course usefull, and I dont see how it should be related to NaN. - anyone

@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

Answered   2023-09-20 20:05:27

  • @Reboog711 The employee's last name is literally the string "Null" as in, "My name is Pat Null" Your answer fails to pass the employee's last name. You answer just hides the fact that "Null" is being inappropriately coerced into the language concept of null by the appendChild() method as described by Ben Burns. The result is still failure of the system to deal with Mr. or Ms. Null. - anyone
  • @MaxxDaymon I think you misconstrue what my answer actually is. It doesn't present a solution; but rather an explanation of why the problem occurs; and quotes relevant code from the Flex Framework. My more recent edit is perhaps misplaced; as it discusses an alternate approach and is not directly related to the original question. - anyone
  • You're sort of on the right track, but at that point in the code content is the string "null", and "null" == null returns false, so that test behaves as intended. Instead I believe the problem is a mix of how XML.appendChild handles a string argument, and how a root XML element containing only the string "null" can be coerced to a literal null. - anyone
  • @Reboog711 Take a look at my fiddle. "null" != null` returning true is desired behavior here. If the opposite happened, this would discard the string "null" from the encoding process, which in fact would be the cause of the problem. However because this test succeeds, the encoder keeps going, until XML.appendChild discards it due to a coercion bug. - anyone
  • No worries. If you want to see the real problem add var xml:XML = <root>null</root>; var s:String = (xml == null) ? "wtf? xml coerced to null?!!" : "xml not coerced to null."; trace(s); to your code sample. - anyone

Translate all characters into their hex-entity equivalents. In this case, Null would be converted into &#4E;&#75;&#6C;&#6C;

Answered   2023-09-20 20:05:27

  • Please don't do this. CDATA was created for use in cases where you need to escape an entire block of text. - anyone
  • I could be wrong, but I don't think downvoting just because it wasn't your solution is how it's supposed to work. Also you have to keep in mind the problem calls for a heuristic solution since there isn't one obvious way, as made evident by the variety of solutions posted. Lastly, keeping in mind I don't know CF, wouldn't a decoder just equate the inner text of <message><![CDATA[NULL]]></message> to the inner text of <message>NULL</message>? If so, then is CDATA really a solution at all? - anyone
  • I downvoted because this is an anti-pattern. The bug in this case isn't in CF, it's in ActionScript. However, you raise a good point nonetheless. I'll add a test to my fiddle for CDATA encoding. - anyone
  • Why all characters? Wouldn't just one be enough? - anyone
  • Good point @flarn2006 and I'm sure that would work too - I guess it just makes more sense to me that something either be all one way or the other for consistency's sake. Someone might come in after you and wonder why only one character is encoded and attempt to "fix" it and thereby break the solution. - anyone

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).

Answered   2023-09-20 20:05:27

  • Yes, there are a number of possibilities here which will require more debugging to narrow down. 1) Is the WSDL used here expressive enough to distinguish between "NULL" as a string value and an actual null (or omitted) value? 2) If so, is the client encoding the last name correctly (as a string and not a null literal) 3) If so, is the service properly interpreting "NULL" as a string, or coercing it to a null value? - anyone

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.

Answered   2023-09-20 20:05:27

  • XXNULLXX could be a name too. You don't know. Maybe people in Indonesia do not have surname and use a variant of XXX as their surname when required. - anyone
  • Same concept, but update all the names in the database and preface then with some character (1Null, 1Smith). Strip off that character in the client. Of course this might be mite work than Reboog's solution. - anyone
  • @BenBurns Yeah, but what if I want to name my kid &#78;&#117;&#108;&#108;? - anyone
  • @Sirens That is not the problem. If my name is "<&quot;>", then I expect it to be properly escaped as &quot;&lt;&amp;quot;&gt;&quot;, that goes without saying. The real problem is with an application behaving as if it uses a blacklist for names. - anyone

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.

Answered   2023-09-20 20:05:27

  • There's good info here, so I won't downvote, but I figured it's worth a comment. By default, XMLEncoder.as will actually encode a true null value properly, by setting xsi:nil="true" on the element. The issue actually appears to be in the way the ActionScript XML type itself (not the encoder) handles the string "null". - anyone

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.

Answered   2023-09-20 20:05:27

  • CDATA was added to the XML spec to avoid these types of kludges. - anyone
  • There is no need to escape "Null" with CDATA, there is no such thing as a null keyword in XML. - anyone
  • Agree with @eckes. I don't understand why there is all this talk of CDATA. CDATA is only useful to escape characters that have special meaning in XML. none of: n, u, l have special semantics in XML. "NULL" and "<![CDATA[NULL]]>" are identical to an XML parser. - anyone
  • @jasonkarns - I agree 100% that there should be nothing special about the string/text node NULL, but to be pedantic, <blah>null</blah> and <blah><![CDATA[null]]> are not the same to an XML parser. They should produce the same results, however the logic flow for handling them is different. It is this effect that we're exploiting as a workaround to the bug in the flex XML implementation. I advocate for this over other approaches as it preserves the readability of the text, and has no side effects for other parsers. - anyone