G'day:
Well at least this time it wasn't something that caught
me out. This one wasted someone else's time.
I keep an eye on
@CFBugNotifier on Twitter (yes: that was a gratuitous plug for my own work), as it's good to know where it's safe to tread in ColdFusion, and where it's better to stay well clear of. I think we've established that ColdFusion's JSON handling is one of those areas to - if not stay clear of - tread with caution. Today a new JSON bug presented itself, in the form of
3596207. This is another example of ColdFusion being unable to correctly identify its own data types. The gist of it is that the string "1." gets
kinda treated like a numeric by ColdFusion, but not really, and in the process CF serialises it incorrectly. There's a repro on the ticket, but I looked into it further. And I'm writing this up as there's some odd behaviour in ColdFusion (as per above), but some unstable behaviour with Railo as well, this time (not relating to JSON though).
Here's my test code:
s = "1.";
try{
param name="s" type="numeric";
paramAsNumeric = true;
}
catch (any e){
paramAsNumeric = {message=e.message,detail=e.detail};
}
try{
param name="s" type="float";
paramAsFloat = true;
}
catch (any e){
paramAsFloat = {message=e.message,detail=e.detail};
}
try{
param name="s" type="integer";
paramAsInteger = true;
}
catch (any e){
paramAsInteger = {message=e.message,detail=e.detail};
}
isNumeric = isNumeric(s);
isValidFloat = isValid("float", s);
isValidInteger = isValid("integer", s);
original = {key=s};
serialised = serializeJson(original);
isValidJson = isJson(serialised);
deserialised = deserializeJson(serialised);
writeDump([
{s=s},
{paramAsNumeric=paramAsNumeric},
{paramAsFloat=paramAsFloat},
{paramAsInteger=paramAsInteger},
{isNumeric=isNumeric},
{isValidFloat=isValidFloat},
{isValidInteger=isValidInteger},
{original=original},
{serialised=serialised},
{isValidJson=isValidJson},
{deserialised=deserialised}
]);
And here's the output (it's the same on CF 9.02 and 10.0.11):
array |
1 |
|
2 |
struct |
PARAMASNUMERIC | true |
|
3 |
|
4 |
struct |
PARAMASINTEGER |
struct |
DETAIL | The value specified, 1., must be a valid integer. |
MESSAGE | Invalid parameter type. |
|
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
struct |
SERIALISED | {"KEY":1.} |
|
10 |
|
11 |
|
I've highlighted a few things:
- I really don't think CF should be treating "1." as a numeric value of any stripe. Numbers don't end with decimal points. Obviously they can have decimal points, but if they do, they need at least one digit after it. That said, it does think this expression is legit:
f = 1.;
And rooting around a bit, actually Java, Javascript, PHP and Python all accept this sort of thing. Ruby doesn't. Hmmm. Well I don't agree with the majority here, but I concede it's an unpopular position to take.
- Next, ColdFusion takes the string "1." and turns it into the "number" 1., and then back into a string 1. All three of those are different things. Only the first one is right, in the given context.
- ColdFusion maintains that JSON is valid. It's not. JSON (or RFC-4627) does not accept that numbers can end in a decimal point. You need to follow the rules, Adobe.
I s'pose I better let ColdFusion off the whole "numbers ending with decimal points" thing, as there's precedent set for it. But not in JSON, and it should
deal with "1." reliably when converting it to JSON (and back). This is another case of CF "not getting" that just cos it's very loosely typed, other systems are not, so it needs to respect the notion of types when it's exporting data.
Now... Railo has a different issue here. Here's the dump from Railo (4.1.0.011):
Array |
1 |
|
2 |
Struct |
PARAMASNUMERIC |
Struct |
DETAIL |
string | Java type of the object is java.lang.String |
|
MESSAGE |
string | Can't cast String [1.] to a value of type [numeric] |
|
|
|
3 |
Struct |
PARAMASFLOAT |
Struct |
DETAIL |
string | Java type of the object is java.lang.String |
|
MESSAGE |
string | Can't cast String [1.] to a value of type [float] |
|
|
|
4 |
Struct |
PARAMASINTEGER |
Struct |
DETAIL |
string | Java type of the object is java.lang.String |
|
MESSAGE |
string | Can't cast String [1.] to a value of type [integer] |
|
|
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
I'm pleased the param "expressions" failed on Railo... although perhaps it should conform with ColdFusion here? I dunno.
However... spot the obvious mistake.
param doesn't accept "1." as any of a numeric, float or integer... but
isValid() thinks "1." is a valid integer. Whilst not being a valid float. Or even numeric. Oops. It's gotta get its messaging straight here!
Railo also reports
isJson() as
true here, but in Railo's case that's correct. It actually creates the JSON properly: it preserves the "1." value all the way through to JSON and back again.
Right. That was too much to write down about "numbers" ending in decimal points, really, wasn't it? What do
you think about 1. being a number? Yes? No? Simply don't care, Adam? ;-)
Cheers.
--
Adam