Utility Function: getProperties
Quite often I have wanted to iterate over the public fields (and getters) of an arbitrary object. Sometimes this is for debugging purposes so I can print out the state of an object, particularly one that has all public fields like a C/C++ structure. Sadly, this is not possibly (with most objects) using the for-in
and for-each
loops we’ve come to know and love. So I made a utility function that I’m sharing with you all today. UPDATE: getProperties now supports MovieClip
derivatives such as library symbols from an FLA
First things first: it’s possible that this utility function has already been created and posted somewhere, but some light internet searching has not turned anything up for me. That said, let’s look at how I implemented the function:
/** * Get the properties of an arbitrary object * @param obj Object to get the properties of * @return A list of the properties of the given object. This list is * empty if the given object has no properties, is null, or is * undefined. * @author Jackson Dunstan, JacksonDunstan.com */ public static function getProperties(obj:*): Vector.<String> { var ret:Vector.<String> = new Vector.<String>(); // Null and undefined have no properties if (obj == null) { return ret; } // Get properties from a for-in loop as long as the object isn't an // Array or Vector var description:XML = describeType(obj); if (!(obj is Array)) { var name:String = String(description.attribute("name")); if (name.indexOf("__AS3__.vec::") < 0) { for (var k:String in obj) { ret.push(k); } } } // Add all properties from the describeType XML. This will be empty // for plain Objects, but they are covered by the above for-in loop var node:XML; for each (node in description.elements("accessor")) { ret.push(node.@name); } for each (node in description.elements("variable")) { ret.push(node.@name); } return ret; }
The function is actually quite small, but there are a few implementation details to note:
null
andundefined
are handled by an==
check withnull
because, unlike ===, the==
operator will do type conversion to convertundefined
tonull
.- The main way of determining the public variables and getters of an arbitrary object is the very handy (and very slow!)
flash.utils.describeType
function. It returns anXML
object that describes the given object very verbosely, which is perfect for our uses. - In the case of dynamic
Objects
(e.g.o = {first:"Jackson", last:"Dunstan"}
),describeType
returns an emptyXML
. Its properties are obtained by thefor-in
loop. - All indices of
Arrays
andVectors
are skipped by exempting them from thefor-in
collection loop. While they are valid properties—in a sense—the returnedVector
would be as long as theArray
orVector
, which is quite undesirable for big lists. - The children of
MovieClip
derivatives, such as library symbols from FLAs, are not present in thedescribeType
results, but are found by thefor-in
loop.
Next up I made a test app to make sure this function works with a variety of inputs: (getProperties omitted for brevity)
package { import flash.text.*; import flash.utils.*; import flash.display.*; public class GetPropertiesTest extends Sprite { private var __logger:TextField = new TextField(); private function log(msg:*): void { __logger.appendText(msg+"\n"); } public function GetPropertiesTest() { __logger.autoSize = TextFieldAutoSize.LEFT; addChild(__logger); var testVals:Object = { sprite: new Sprite(), instance: new MyClass(), dynInstance: new MyDynamicClass(), array: [2,4,6,8], vector: Vector.<int>([2,4,6,8]), object: {first:"Jackson", last:"Dunstan"}, dynFunction: function(arg1:int, arg2:int): void { }, method: myMethod, number: 3.0, bool: true, classObj: MyClass }; for (var testValProp:String in testVals) { var testVal:* = testVals[testValProp]; var props:Vector.<String> = getProperties(testVal); log("Properties of " + testValProp + ":"); for each (var prop:String in props) { log("\t" + prop); } } } private function myMethod(arg1:int, arg2:int): void { } } } import flash.display.*; class MyParent { public var publicVarParent:int; public static var publicStaticVarParent:int; private var privateVarParent:int; private static var privateStaticVarParent:int; private var protectedVarParent:int; private static var protectedStaticVarParent:int; } class MyClass extends MyParent { public var publicVar:int; public static var publicStaticVar:int; private var privateVar:int; private static var privateStaticVar:int; private var protectedVar:int; private static var protectedStaticVar:int; } dynamic class MyDynamicClass extends MyParent { public var publicVar:int; public static var publicStaticVar:int; private var privateVar:int; private static var privateStaticVar:int; private var protectedVar:int; private static var protectedStaticVar:int; }
If you run this test yourself, you will probably have to use the context menu to “select all” and “copy” in order to get the whole output. Also, the order of the property names returned by getProperties
is undefined, so you might not get results in the same order as I do. That said, here’s what I see with Flash Player plugin 10.1.102.64 on Mac OS X 10.6:
Properties of dynInstance: publicVar publicVarParent Properties of array: length Properties of classObj: prototype publicStaticVar Properties of vector: fixed length Properties of method: prototype length Properties of bool: Properties of sprite: scale9Grid soundTransform parent loaderInfo useHandCursor accessibilityProperties scaleX scaleY alpha blendShader buttonMode dropTarget numChildren textSnapshot width tabChildren height mouseChildren tabEnabled hitArea tabIndex focusRect root stage x y z visible name scaleZ mask mouseX graphics rotation mouseY rotationX rotationY rotationZ mouseEnabled cacheAsBitmap doubleClickEnabled opaqueBackground accessibilityImplementation scrollRect contextMenu filters blendMode transform Properties of object: last first Properties of number: Properties of dynFunction: prototype length Properties of instance: publicVar publicVarParent
I hope you find getProperties
useful!
#1 by as3isolib on December 6th, 2010 ·
thanks Jackson! One thing you might consider are for dynamic subclasses such as a movieClip or ObjectProxy. On first glance, it looks like your conditional
would be skipped for those kind of objects.
Keep up the good work. One of my daily top 5 blogs!!!
#2 by jackson on December 6th, 2010 ·
Very good catch! I’ve updated
getProperties
and the article to handle this case. I’ve tested it with a simple FLA and it seems to work just fine. I also regressed against the test cases from the article, but didn’t update the test app in the article. Interestingly enough, this case isn’t triggered by all dynamic subclasses, as this worked just fine:Thanks again for the catch and the compliments!
#3 by Craig Murray on December 6th, 2010 ·
@as3iso: what are your other 4 top blogs? :)
#4 by as3isolib on December 7th, 2010 ·
well a few aren’t development related but here is what I check daily for updates:
– http://www.jacksondunstan.com
– http://www.futilitycloset.com/ (interesting facts)
– http://www.makeuseof.com/ (lots of sub articles on tech in general)
– http://angrierchairs.blogspot.com/ (music blog)
– http://www.loadown.org/ (music blog)
I also occasionally check the adobe blog aggregator
– http://weblogs.macromedia.com/mxna/
#5 by pnico on December 13th, 2010 ·
There are a couple reflection libraries for as3 that I’ve found helpful for this sort of thing. also if you’re accessing the reflection data a lot, they’re helpful for performance (maybe not for memory usage though I guess) because the output of describeType is cached:
as3commons-reflect http://code.google.com/p/as3-commons/
spicelib (part of Parsley) http://www.spicefactory.org/parsley/index.php
#6 by jackson on December 13th, 2010 ·
I knew there had to be some out there! I like the as3commons-reflect API, but it’s a pretty heavyweight alternative to
getProperties
since it’s giving you aType
with everything described in it, not just the properties. That said, most of the time it probably won’t matter and it’s not likegetProperties
is fast in the first place.Thanks for the very informative links!
#7 by pnico on December 13th, 2010 ·
Yeah – they’re all really just wrappers around the XML you get from describeType. It’s kind of weird that Flash makes you go through that, but at least it’s there, and helpful sometimes
#8 by pnico on December 15th, 2010 ·
Oh and hey, check this out..
http://www.tillschneidereit.de/2009/11/22/improved-reflection-support-in-flash-player-10-1/