One of the new features in Flash Player 11 is a native JSON encoder/decoder class. In the Serialize Anything article, I neglected to add JSON as an option for serializing and deserializing arbitrary objects. In today’s followup we’ll take a look at the performance of the native JSON class and compare it to ByteArray.readObject/writeObject and XML.

The built-in JSON class has only two static methods: stringify (serialize) and parse (deserialize). The following test app is based on the test app from the first article with a few changes:

  • JSON.stringify added as a serialization option
  • Manual JSON string building added as a serialization option
  • JSON.parse added as a deserialization option
  • Reporting compressed serialized data size to get a more detailed picture of the serialization options
  • Here is the test app:

    package
    {
    	import flash.display.*;
    	import flash.utils.*;
    	import flash.text.*;
    	import flash.net.*;
     
    	public class SerializeAnything2 extends Sprite
    	{
    		private var __logger:TextField = new TextField();
    		private function log(msg:*): void { __logger.appendText(msg + "\n"); }
    		private function row(...cols): void
    		{
    			__logger.appendText(cols.join(",")+"\n");
    		}
     
    		public function SerializeAnything2()
    		{
    			stage.align = StageAlign.TOP_LEFT;
    			stage.scaleMode = StageScaleMode.NO_SCALE;
     
    			var logger:TextField = __logger;
    			logger.autoSize = TextFieldAutoSize.LEFT;
    			addChild(logger);
     
    			var beforeTime:int;
    			var afterTime:int;
    			var i:int;
    			var SIZE:int = 100000;
     
    			registerClassAlias("Person", Person);
     
    			var people:Vector.<Person> = new Vector.<Person>(SIZE);
    			for (i = 0; i < SIZE; ++i)
    			{
    				var person:Person = new Person();
    				person.secret = "I'm a Canadian";
    				person.first = "William";
    				person.last = "Shatner";
    				person.age = 80;
     
    				people[i] = person;
    			}
     
    			row("Operation", "Time");
     
    			beforeTime = getTimer();
    			var ba:ByteArray = new ByteArray();
    			ba.writeObject(people);
    			afterTime = getTimer();
    			row("ByteArray serialize", (afterTime-beforeTime));
     
    			beforeTime = getTimer();
    			var jsonString:String = "[";
    			for each (person in people)
    			{
    				jsonString += "{\"first\":\"" + person.first
    					+ "\",\"last\":\"" + person.last
    					+ "\",\"age\":" + person.age
    					+ ",\"secret\":\"" + person.secret + "\"},";
    			}
    			jsonString = jsonString.substr(0, jsonString.length-1) + "]";
    			afterTime = getTimer();
    			row("JSON serialize (manual)", (afterTime-beforeTime));
     
    			beforeTime = getTimer();
    			jsonString = JSON.stringify(people);
    			afterTime = getTimer();
    			row("JSON serialize (stringify)", (afterTime-beforeTime));
     
    			beforeTime = getTimer();
    			var xmlString:String = "<people>";
    			for each (person in people)
    			{
    				xmlString += "<person first=\"" + person.first
    						+ "\" last=\"" + person.last
    						+ "\" age=\"" + person.age
    						+ "\" secret=\"" + person.secret + "\" />";
    			}
    			xmlString += "</people>";
    			var xml:XML = new XML(xmlString);
    			afterTime = getTimer();
    			row("XML serialize", (afterTime-beforeTime));
     
    			beforeTime = getTimer();
    			ba.position = 0;
    			people = ba.readObject() as Vector.<Person>;
    			afterTime = getTimer();
    			row("ByteArray deserialize", (afterTime-beforeTime));
     
    			beforeTime = getTimer();
    			people = new Vector.<Person>();
    			var jsonPeople:Array = JSON.parse(jsonString) as Array;
    			for each (var jsonPerson:Object in jsonPeople)
    			{
    				person = new Person();
    				person.first = jsonPerson.first;
    				person.last = jsonPerson.last;
    				person.age = jsonPerson.age;
    				person.secret = jsonPerson.secret;
    				people.push(person);
    			}
    			afterTime = getTimer();
    			row("JSON deserialize", (afterTime-beforeTime));
     
    			beforeTime = getTimer();
    			people = new Vector.<Person>();
    			for each (var personNode:XML in xml.person)
    			{
    				person = new Person();
    				person.first = personNode.@first;
    				person.last = personNode.@last;
    				person.age = personNode.@age;
    				person.secret = personNode.@secret;
    				people.push(person);
    			}
    			afterTime = getTimer();
    			row("XML deserialize", (afterTime-beforeTime));
     
    			row();
     
    			row("Format", "Uncompressed Size", "Compressed Size");
    			var uncompressedSize:int = ba.length;
    			ba.position = 0;
    			ba.compress();
    			row("ByteArray", uncompressedSize, ba.length);
    			ba.length = 0;
    			ba.writeMultiByte(jsonString, "us-ascii");
    			ba.position = 0;
    			uncompressedSize = ba.length;
    			ba.compress();
    			row("JSON", uncompressedSize, ba.length);
    			ba.length = 0;
    			ba.writeMultiByte(xmlString, "us-ascii");
    			ba.position = 0;
    			uncompressedSize = ba.length;
    			ba.compress();
    			row("XML", uncompressedSize, ba.length);
    		}
    	}
    }
    class Person
    {
    	public var first:String;
    	public var last:String;
    	private var __secret:String;
    	public var age:int;
     
    	public function set secret(secret:String): void { __secret = secret; }
    	public function get secret(): String { return __secret; }
    }

    I ran this test with the following environment:

    • Flex SDK (MXMLC) 4.5.1.21328, compiling in release mode (no debugging or verbose stack traces)
    • Release version of Flash Player 11.1.102.55
    • 2.4 Ghz Intel Core i5
    • Mac OS X 10.7.2

    And got these results:

    Operation Time
    ByteArray serialize 125
    JSON serialize (manual) 178
    JSON serialize (stringify) 1939
    XML serialize 591
    ByteArray deserialize 103
    JSON deserialize 577
    XML deserialize 451

    Serialization and Deserialization Performance Chart

    Format Uncompressed Size Compressed Size
    ByteArray 1000063 2048
    JSON 7200001 24528
    XML 7400017 25220

    Serialization Format Sizes Chart

    So we see that this test has revealed some interesting data about serialization and deserialization:

    • Manual creation of JSON is much (~10x) faster than JSON.stringify (Update: this manual creation routine does not escape strings. A version that escapes strings will be somewhat slower.)
    • Manual creation of JSON is quite a bit (~3x) quicker than manual creation of XML.
    • ByteArray.readObject and ByteArray.writeObject are still the speed kings for serialization and deserialization
    • JSON is slightly smaller in both uncompressed and compressed form
    • ByteArray is about 7x smaller than either JSON or XML in uncompressed form and 10x smaller in compressed form

    The obvious conclusion from the above is that ByteArray is the superior serialization format when compared to JSON and XML. It’s faster to serialize, faster to deserialize, smaller when either uncompressed or compressed, supports AS3 types via flash.net.registerClassAlias, and requires no AS3 code to serialize it quickly. What more could you want?

    Spot a bug? Have a tip about serialization or deserialization? Post a comment!