package com.adobe.serialization.json
{
import flexunit.framework.TestCase;
public class JSONTest extends TestCase
{
public function JSONTest( methodName:String = null )
{
super( methodName );
}
/**
* Helper method to verify whether or not a given input correctly
* throws a JSONParseError
*/
protected function expectParseError( jsonString:String, strict:Boolean = true ):void
{
var parseError:JSONParseError = null;
try
{
var o:* = JSON.decode( jsonString, strict );
fail( "Expecting parse error but one was not thrown" );
}
catch ( e:JSONParseError )
{
parseError = e;
}
catch ( e:Error )
{
throw e;
}
assertNotNull( parseError );
}
/**
* Test for the JSON string true decoded to boolean true.
*/
public function testDecodeTrue():void
{
var o:* = JSON.decode( "true" ) as Boolean;
assertTrue( "Expected decoded true", o );
}
public function testDecodeFalse():void
{
var o:* = JSON.decode( "false" ) as Boolean;
assertFalse( "Expected decoded false", o );
}
public function testDecodeNull():void
{
var o:* = JSON.decode( "null " );
assertNull( "Expected decoded null", o );
}
public function testDecodeString():void
{
var string:String = "this \"is\" \t a \/ string \b \f \r \h \\ \n with \' ch\\u0061rs that should be { } http://escaped.com/";
assertEquals( string, JSON.decode( JSON.encode( string ) ) );
var o:String = JSON.decode( "\"http:\/\/digg.com\/security\/Simple_Digg_Hack\"" ) as String;
assertEquals( "String not decoded correctly", "http://digg.com/security/Simple_Digg_Hack", o );
expectParseError( "\"unterminated string" );
}
public function testDecodeStringWithInvalidUnicodeEscape():void
{
expectParseError( "\"\\u\"" );
expectParseError( "\"\\ut\"" );
expectParseError( "\"\\u123\"" );
assertEquals( "a", JSON.decode( "\"\\u0061\"" ) );
}
public function testDecodeStringWithControlCharacters():void
{
var i:int;
for ( i = 0x00; i <= 0x1F; i++ )
{
expectParseError( "\"string with control char " + i + ": " + String.fromCharCode( i ) + "\"", true );
}
for ( i = 0x00; i <= 0x1F; i++ )
{
var string:String = "control char " + i + ": " + String.fromCharCode( i ) + ""
assertEquals( string,
JSON.decode( "\"" + string + "\"", false ) );
}
}
public function testDecodeZero():void
{
var n:Number = JSON.decode( "0" ) as Number;
assertEquals( n, 0 );
}
public function testDecodeZeroWithDigitsAfterIt():void
{
expectParseError( "02" );
}
public function testDecodePositiveInt():void
{
var n:int = JSON.decode( "123871" ) as int;
assertEquals( n, 123871 );
}
public function testDecodeNegativeInt():void
{
var n:int = JSON.decode( "-97123" ) as int;
assertEquals( n, -97123 );
}
public function testDecodePositiveFloat():void
{
var n:Number = JSON.decode( "12.987324" ) as Number;
assertEquals( n, 12.987324 );
}
public function testDecodeNegativeFloat():void
{
var n:Number = JSON.decode( "-1298.7324" ) as Number;
assertEquals( n, -1298.7324 );
}
public function testDecodeFloatLeadingZeroError():void
{
expectParseError( "-.2" );
}
public function testDecodeFloatDecimalMissingError():void
{
expectParseError( "1." );
}
public function testDecodeScientificRegularExponent():void
{
var n:Number = JSON.decode( "6.02e2" ) as Number;
assertEquals( n, 602 );
n = JSON.decode( "-2e10" );
assertEquals( n, -20000000000 );
assertEquals( n, -2 * Math.pow( 10, 10 ) );
}
public function testDecodeScientificPositiveExponent():void
{
var n:Number = JSON.decode( "2E+9" ) as Number;
assertEquals( n, 2 * Math.pow( 10, 9 ) );
n = JSON.decode( "-2.2E+23" ) as Number;
assertEquals( n, -2.2 * Math.pow( 10, 23 ) );
}
public function testDecodeScientificNegativeExponent():void
{
var n:Number = JSON.decode( "6.02e-23" ) as Number;
assertEquals( n, 6.02 * Math.pow( 10, -23 ) );
n = JSON.decode( "-4e-9" ) as Number;
assertEquals( n, -4 * Math.pow( 10, -9 ) );
n = JSON.decode( "0E-2" ) as Number;
assertEquals( n, 0 );
}
public function testDecodeScientificExponentError():void
{
expectParseError( "1e" );
}
/**
* In non-strict mode, we can interpret hex values as numbers
*/
public function testDecodeHex():void
{
var n:Number = JSON.decode( "0xFF0033", false );
assertEquals( 0xFF0033, n );
expectParseError( "0x", false );
expectParseError( "0xZ", false );
expectParseError( "0xz", false );
expectParseError( "0xFF0033" );
}
/**
* Non-strict mode allows for NaN as a valid token
*/
public function testDecodeNaN():void
{
var n:Number = JSON.decode( "NaN", false ) as Number;
assertTrue( isNaN( n ) );
var o:Object = JSON.decode( "{ \"num\": NaN }", false );
assertNotNull( o );
assertTrue( isNaN( o.num ) );
expectParseError( "NaN" );
expectParseError( "{ \"num\": NaN }" );
}
public function testDecodeObject():void
{
var o:* = JSON.decode( " { \"test\": true, \"test2\": -12356732.12 } " ) as Object;
assertNotNull( o );
assertEquals( "Expected decoded object.test = true", true, o.test );
assertEquals( "Expected decoded object.test2 = -12356732.12", -12356732.12, o.test2 );
}
/**
* In non-strict mode, the object can have a trailing comma after
* the last member and not throw an error.
*/
public function testDecodeObjectWithTrailingComma():void
{
var o:Object = JSON.decode( "{\"p1\":true,\"p2\":false,}", false );
assertNotNull( o );
assertTrue( o.p1 );
assertFalse( o.p2 );
o = JSON.decode( "{,}", false );
assertNotNull( o );
expectParseError( "{\"p1\":true,\"p2\":false,}" );
expectParseError( "{,}" );
}
/**
* Leading comma is not supported (yet? should they be?)
*/
public function testDecodeObjectWithLeadingCommaFails():void
{
expectParseError( "[,\"p1\":true}", false );
}
public function testDecodeArray():void
{
var o:* = JSON.decode( " [ null, true, false, 100, -100, \"test\", { \"foo\": \"bar\" } ] " ) as Array;
assertNull( "Expected decoded array[0] == null", o[0] );
assertTrue( "Expected decoded array[1] == true", o[1] );
assertFalse( "Expected decoded array[2] == false", o[2] );
assertEquals( "Expected decoded array[3] == 100", 100, o[3] );
assertEquals( "Expected decoded array[4] == -100", -100, o[4] );
assertEquals( "Expected decoded array[5] == \"test\"", "test", o[5] );
assertEquals( "Expected decoded array[6].foo == \"bar\"", "bar", o[6].foo );
}
public function testDecodeArrayWithNewlines():void
{
var o:Array = JSON.decode( "\n [ \nnull, \n \r \t \r true \n ] \r \t \r \n" ) as Array;
assertNull( "Expected decoded with newlines array[0] == null", o[0] );
assertTrue( "Expected decoded with newlines array[1] == true", o[1] );
}
/**
* In non-strict mode, the array can have a trailing comma after
* the last element and not throw an error.
*/
public function testDecodeArrayWithTrailingComma():void
{
var o:Array = JSON.decode( "[0,1,]", false ) as Array;
assertEquals( 0, o[0] );
assertEquals( 1, o[1] );
o = JSON.decode( "[,]", false ) as Array;
assertNotNull( o );
assertEquals( 0, o.length );
expectParseError( "[0,1,]" );
expectParseError( "[,]" );
}
/**
* Leading comma is not supported (yet? should they be?)
*/
public function testDecodeArrayWithLeadingCommaFails():void
{
expectParseError( "[,10]", false );
}
public function testDecodeComments():void
{
expectParseError( "/*" );
expectParseError( "/*/" );
expectParseError( "//" );
var n:Number = JSON.decode( "// this is a number\n12" );
assertEquals( 12, n );
n = JSON.decode( "/* \n\n multiine com//ment *///\n14" );
assertEquals( 14, n )
}
public function testDecodeSuccessiveComments():void
{
var jsonString:String = " // test comment"
+ "\n // test comment line 2"
+ "\nfalse";
var o:* = JSON.decode( jsonString );
assertEquals( false, o );
}
public function testDecodeEmptyStringError():void
{
expectParseError( "" );
}
public function testDecodeWhiteSpace():void
{
var n:Number;
var nbsp:String = String.fromCharCode( 160 );
n = JSON.decode( " 1 " );
assertEquals( 1, n );
n = JSON.decode( "\t2\t" );
assertEquals( 2, n );
n = JSON.decode( "\r3\r" );
assertEquals( 3, n );
n = JSON.decode( "\n4\n" );
assertEquals( 4, n );
n = JSON.decode( "\t \n\n\r \r\n\t 100 \r\n\t\r\r\r\n \n" ) as Number
assertEquals( 100, n );
n = JSON.decode( "\t \n"
+ nbsp
+ "\n\r \r\n\t 100 \r\n\t\r\r\r\n"
+ nbsp
+ " \n", false ) as Number
assertEquals( 100, n );
expectParseError( "\t \n" + nbsp + "\n\r \r\n\t 100 \r\n\t\r\r\r\n" + nbsp + " \n" );
}
public function testDecodeWithCharactersLeftInInputString():void
{
expectParseError( "[ 1 ] true" );
expectParseError( "0xZ" );
expectParseError( "true Z" );
var a:Array = JSON.decode( "[ 1 ] true", false ) as Array;
assertEquals( 1, a[0] );
var n:Number = JSON.decode( "1Z", false ) as Number;
assertEquals( 1, n );
var b:Boolean = JSON.decode( "true Z", false ) as Boolean;
assertTrue( b );
}
public function testDecodeWithUnexpectedEndOfInput():void
{
expectParseError( "[" );
expectParseError( "[ 12" );
expectParseError( "[ 12, " );
expectParseError( "{" );
expectParseError( "{ \"prop" );
expectParseError( "{ \"prop\"" );
expectParseError( "{ \"prop\":" );
expectParseError( "{ \"prop\": t" );
expectParseError( "{ \"prop\": true" );
expectParseError( "t" );
expectParseError( "tr" );
expectParseError( "tru" );
}
public function testEncodeTrue():void
{
var o:String = JSON.encode( true );
assertEquals( "Expected encoded true", "true", o );
}
public function testEncodeFalse():void
{
var o:String = JSON.encode( false );
assertEquals( "Expected encoded false", "false", o );
}
public function testEncodeNull():void
{
var o:String = JSON.encode( null );
assertEquals( "Expected encoded null", "null", o );
}
public function testEncodeString():void
{
var o:String = JSON.encode( "this is a \n \"string\"" );
assertEquals( "Expected encoded string", "\"this is a \\n \\\"string\\\"\"", o );
o = JSON.encode( "myString" );
assertEquals( "\"myString\"", o );
}
public function testEncodeArrayEmpty():void
{
var o:String = JSON.encode( [] );
assertEquals( "Expected encoded []", "[]", o );
}
public function testEncodeArray():void
{
var o:String = JSON.encode( [ true, false, -10, null, Number.NEGATIVE_INFINITY ] );
assertTrue( "Expected encoded array", "[true,false,-10,null,null]", o );
}
public function testEncodeObjectEmpty():void
{
var o:String = JSON.encode( {} );
assertTrue( "Expected encoded {}", "{}", o );
}
public function testEncodeObject():void
{
var obj:Object = { foo: { foo2: { foo3: { foo4: "bar" } } } };
var s:String = JSON.encode( obj );
assertEquals( "Deeply nested", "{\"foo\":{\"foo2\":{\"foo3\":{\"foo4\":\"bar\"}}}}", s );
obj = new Object();
obj[" prop with spaces "] = true;
s = JSON.encode( obj );
assertEquals( "Prop with spaces", "{\" prop with spaces \":true}", s );
}
public function testEncodeClassInstance():void
{
var customObject:SimpleClass = new SimpleClass();
customObject.transientVar = "Should not be encoded";
var s:String = JSON.encode( customObject );
assertTrue( "Has length", s.length > 0 );
assertEquals( "Should not find transient var in string", -1, s.indexOf( "\"transientVar\":\"Should not be encoded\"" ) );
var o:Object = JSON.decode( s );
assertNotNull( o );
assertNotNull( o.publicVar1 );
assertNotNull( o.publicVar2 );
assertNotNull( o.accessor1 );
assertNotNull( o.accessor2 );
assertNull( o.transientVar );
assertEquals( 17, o.publicVar1 );
assertEquals( 20, o.publicVar2 );
assertEquals( 25, o.accessor1 );
assertEquals( 30, o.accessor2 );
var count:int = 0;
for ( var key:String in o )
{
count++;
}
assertEquals( 4, count );
}
}
}