To save precious bandwidth, keeping your AMF data size small is crucial. By default, AMF has a tendency to create bloated data sizes. In the last article, I showed a way to shrink your class names to a single character when stored in AMF data. Today I’ll show a you a trick to shrink your class field names to a single character. Read on to find out how.

While registerClassAlias can be used to keep the class name to a single character, it won’t do anything for field names. However, I’ve derived a trick to avoid the need for you to rename all of your field names to unreadable single characters. Consider this class, a modified version of the class used last time:

package net.somebody.foo.goo.utils
{
	public class SomeClassWithAReallyReallyLongName
	{
		public var integerValueWithAReallyLongName:int;
		public var stringValueWithAReallyLongName:String;
		public var booleanValueWithAReallyLongName:Boolean;
		public var numberValueWithAReallyLongName:Number;
	}
}

Hopefully your field names aren’t this long, but they’re almost always going to be longer than a single character. Serializing these long names will quickly eat up AMF size, so let’s apply my trick to cut the size back down. First, we make use of the [Transient] metadata tag supported in both ASC 1.0 and ASC 2.0. One very confusing caveat is that you need to use an option from the “advanced” section of ASC 2.0’s command line help or it will skip the [Transient] metadata tag completely:

mxmlc MyApp.as -compiler.keep-as3-metadata Transient

So what is [Transient], anyhow? Simple. All it does is tell the AMF serializer to skip the field you put it before. With that in mind, we can skip the fields with the really long names and put a getter and setter with a single name in addition:

package net.somebody.foo.goo.utils
{
	public class TinyClassWithAReallyReallyLongName
	{
		[Transient]
		public var integerValueWithAReallyLongName:int;
		public function get i(): int
		{
			return integerValueWithAReallyLongName;
		}
		public function set i(val:int): void
		{
			integerValueWithAReallyLongName = val;
		}
 
		[Transient]
		public var stringValueWithAReallyLongName:String;
		public function get s(): String
		{
			return stringValueWithAReallyLongName;
		}
		public function set s(val:String): void
		{
			stringValueWithAReallyLongName = val;
		}
 
		[Transient]
		public var booleanValueWithAReallyLongName:Boolean;
		public function get b(): Boolean
		{
			return booleanValueWithAReallyLongName;
		}
		public function set b(val:Boolean): void
		{
			booleanValueWithAReallyLongName = val;
		}
 
		[Transient]
		public var numberValueWithAReallyLongName:Number;
		public function get n(): Number
		{
			return numberValueWithAReallyLongName;
		}
		public function set n(val:Number): void
		{
			numberValueWithAReallyLongName = val;
		}
	}
}

Getters and setters will be used during serialization and deserialization, respectively. Their names will also be used, so we can simply give them a single-character name like in the previous article. These getters and setters can be used by the rest of your app too since there’s nothing special about them, but I’d advise against it for the sake of code clarity and avoiding the function call overhead.

The following little test app checks the size of the above two classes as a “plain”/untyped AMF object, a typed AMF object with a fully-qualified name, and a typed AMF object with a single-character name:

package
{
	import flash.display.Sprite;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.net.registerClassAlias;
	import flash.utils.ByteArray;
 
	import net.somebody.foo.goo.utils.SomeClassWithAReallyReallyLongName;
	import net.somebody.foo.goo.utils.TinyClassWithAReallyReallyLongName;
 
	public class AMFSize extends Sprite
	{
		private var logger:TextField = new TextField();
		private function row(...cols): void
		{
			logger.appendText(cols.join(",")+"\n");
		}
 
		public function AMFSize()
		{
			logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(logger);
 
			row("Class", "Plain Size", "Full Name Size", "One Letter Size");
			var plainBytes:ByteArray;
			var longBytes:ByteArray;
			var shortBytes:ByteArray;
 
			///////////////////////////////
			// SomeClassWithAReallyLongName
			///////////////////////////////
 
			plainBytes = new ByteArray();
			plainBytes.writeObject(new SomeClassWithAReallyReallyLongName());
 
			registerClassAlias(
				"net.somebody.foo.goo.utils.SomeClassWithAReallyReallyLongName",
				SomeClassWithAReallyReallyLongName
			);
			longBytes = new ByteArray();
			longBytes.writeObject(new SomeClassWithAReallyReallyLongName());
 
			registerClassAlias("C", SomeClassWithAReallyReallyLongName);
			shortBytes = new ByteArray();
			shortBytes.writeObject(new SomeClassWithAReallyReallyLongName());
 
			row(
				"SomeClassWithAReallyReallyLongName",
				plainBytes.length,
				longBytes.length,
				shortBytes.length
			);
 
			///////////////////////////////
			// TinyClassWithAReallyLongName
			///////////////////////////////
 
			plainBytes = new ByteArray();
			plainBytes.writeObject(new TinyClassWithAReallyReallyLongName());
 
			registerClassAlias(
				"net.somebody.foo.goo.utils.TinyClassWithAReallyReallyLongName",
				TinyClassWithAReallyReallyLongName
			);
			longBytes = new ByteArray();
			longBytes.writeObject(new TinyClassWithAReallyReallyLongName());
 
			registerClassAlias("C", TinyClassWithAReallyReallyLongName);
			shortBytes = new ByteArray();
			shortBytes.writeObject(new TinyClassWithAReallyReallyLongName());
 
			row(
				"TinyClassWithAReallyReallyLongName",
				plainBytes.length,
				longBytes.length,
				shortBytes.length
			);
		}
	}
}

Here are the results:

Class Plain Size Full Name Size One Letter Size
SomeClassWithAReallyReallyLongName 142 203 143
TinyClassWithAReallyReallyLongName 24 85 25

Field Size Comparison Chart

This shows that we’ve got a substantial savings using this trick. In this case the best size goes from 142 bytes to just 24, nearly a 6x reduction! The typed version is just one byte larger.

So If you’re willing to add some single-character getters and setters you could really save a bundle on your AMF data size!

Spot a bug? Have a question or suggestion? Post a comment!