Here are four simple data types that are provided by AMF3. In the binary protocol, each of these data types is transmitted as a 1-byte type marker. The corresponding XML tags for each data type are included in parenthesis.
<Integer>123456</Integer>
The integer data type is encoded as a 1-byte type marker, followed by up to 4 bytes that encode the integer using the variable length format. Besides their use as a data type, variable-length integers are used in other places within the protocol.
<Double>3.14159</Double>
In the binary format, doubles are encoded as 1-byte type markers followed by 8 bytes that contain the double value.
Strings are used to store all types of UTF-8-encoded string data. Besides their use as a data type, strings are also used to specify string data, such as classes or member names. All string values in an AMF3 data stream are put into a reference table which is typically generated when AMF3 or XML data is parsed.
A string can either be encoded as a string value or as a string reference. A string reference refers to a previous occurrence of its number in the string reference table. This feature is used to save space when the same string value occurs several times in a single AMF3 data stream.
<String>Hello, World!</String>
<String val-refId="#0">This string is referenced</String> <String val-ref="#0"></String>
AMF3 provides two data types to encode XML data: XMLDocument, which represents a Flash legacy data type, and XML, which is a newer XML type. Both data types encode XML as strings. The difference is that these strings are not registered in the string reference table. They are registered in the object reference table.
<Xml><Data>some data</Data></Xml> <XmlDoc><Data>some more data</Data></XmlDoc>
As with strings, XML data can be referenced to save space:
<Xml refId="data"><Data>foobar</Data></Xml> <Xml ref="data"></Xml>
In AMF3, the date is encoded as the number of milliseconds elapsed since January 1st 1970, 00:00, in the UTC timezone. The number of milliseconds is encoded as a double. Dates also support references, and each date is registered in the object reference table.
<Date>2007-02-18 11:29:00.000</Date> <Date refId="newmillenium">2000-01-01 00:00:00.000</Date> <Date ref="newmillenium"></Date>
Arrays in AMF3 cover two types of data structures: standard arrays, where each element is identified by an index number, and associative arrays, where each element is identified by a key (in the case of AMF3, this is always a string). In the binary representation of an array, the associative portion of the array is listed first. This is followed by a separator (an empty string) and finally the dense portion of the array (the standard array elements).
In XML representation, the elements of an associative array are marked by their name attributes. Generally, arrays are not bound to a specific type, which means that you can put any data type in them, including different data types.
<Array> <Integer name="number1">23</Integer> <Integer name="number2">42</Integer> <String name="username">EMEA\johndoe</String> <Date>2008-02-17 13:47:13.000</Date> <Undefined></Undefined> </Array>
In the above example there is an array with three elements that have a name attribute (meaning they belong to the associative portion of the array) and two elements that do not have a name attribute, which means that they belong to the dense portion of the array.
As with other data types, arrays can be referenced; their elements can be referenced; even their elements’ names can be referenced:
<Array> <String name="foo" name-refId="#0">bar</String> <String val-ref="#0"></String> </Array>
In this example, there is an array with two string elements. The first element has the name foo and the value bar. The second element has no name, but references the string with the string reference table ID #0 (the string foo).
Arrays themselves are registered in the object reference table.
Objects are the most powerful data type within AMF3. There are several subtypes of classes that you need to be aware of. What all object types have in common is that they bear a class name.
Object Traits
The traits of an object are its member names, plus information indicating if an object is dynamic or externalizable.
An object with traits contains a list of member names followed by the corresponding members. In binary format, member names are encoded as strings without type markers. The number of member names is encoded in an additional field of flags that also contains information about the object’s sub type. Following the members, an optional, additional list of dynamic members, each preceded by their respective member name, is included. The list of dynamic members is only included if the dynamic flag is set to true and terminated by an empty string.
<Object classname="testclass"> <Member>strFirstName</Member> <Member>strSurname</Member> <String>John</String> <String>Doe</String> </Object> <Object classname="testclass2" dynamic="true"> <Integer name="answer">42</Integer> </Object>
Object References
Objects can be directly referenced because they are registered in the objects reference table.
<Object classname="testclass3" dynamic="true" refId="#0"> <Integer name="answer">42</Integer> </Object> <Object ref="#0"></Object>
Traits References
Each object of subtype object traits or externalizable traits is not only added to the objects reference table, but also to the traits reference table. Other objects can then refer to the traits of these objects. This means that such an object does not need to come with the list of members and the object flags by itself, but only with the actual members.
<Object classname="nameclass" traits-refId="name"> <Member>userid</Member> <Member>firstName</Member> <Member>lastName</Member> <String>jdoe</String> <String>John</String> <String>Doe</String> </Object> <Object traits-ref="name"> <String>mmustermann</String> <String>Max</String> <String>Mustermann</String> </Object>
Externalizable Traits
Externalizable traits contain the binary representation of an object, an indeterminate number of bytes serialized in an unknown format. The format depends on the class that serializes and deserializes this data. The class also has to know how many bytes it has to consume from the byte stream.
Silk Performer provides support for the three most common classes:
According to Adobe, these three classes are the most common, and are encoded as AMF3. This means that these three classes are parsed like normal arrays or objects, respectively.
In case a class other than these three is found, it is assumed that all bytes up to the end of the stream belong to the externalizable traits. The content of such an unknown externalizable trait is stored as Base64-encoded data.
<Object classname="flex.messaging.io.ArrayCollection" externalizable="true"> <Array> <Integer>1</Integer> <Integer>2</Integer> <Integer>3</Integer> <Integer>4</Integer> </Array> </Object> <Object classname="flex.messaging.io.ArrayList" externalizable="true"> <Array> <String>hugo</String> <String>hugo2</String> </Array> </Object> <Object classname="flex.messaging.io.ObjectProxy" externalizable="true"> <Object classname="" dynamic="true"> <String name="ssnum">555-55-5555</String> <String name="name">Tyler</String> <Integer name="age">5</Integer> </Object> </Object> <Object classname="test" externalizable="true">SGVsbG8sIHdvcmxk</Object>
A ByteArray holds an array of bytes. In XML representation, binary data is encoded in Base64 format.
ByteArrays are registered in the object reference table.
<ByteArray>SGVsbG8sIHdvcmxk</ByteArray>
Three different reference tables are used. The following list offers an overview of which data types are registered in which reference table.
String Reference Table:
Object Reference Table:
Traits Reference Table:
Not registered in any reference table:
Reference IDs are generated by the AMF3 parser. However in hand-written XML documents, you can use custom reference IDs:
<String val-refId="username">EMEA\johndoe</String> <String val-ref="username"></String>