Actionscript 3.0 String With Format? - actionscript-3

How can i format a string with supplied variables in AS3?
//vars
var myNumber:Number = 12;
var myString:String = "Months";
var myObject:MovieClip = year;
//string
myString.txt = "One (?) consists of (?) consecutive (?)", string(myObject), string(myNumber), myString;
so in the string above, i would like myString to display "One year consists of 12 consecutive Months", but i'm new to AS3 and do not know how to properly format a string.
i'm sure that i'll have to cast the number variable into a string, string(myNumber), but i don't know if casting a movie clip variable to a string, string(myMovieClip), will return the name of the movie clip or produce an error. i'm willing to bet on the later.

The answers to this similar question suggest using the Formatter class or StringUtil.substitute().
The latter looks the simplest; in your case you would use it like this:
var str:String = "One {0} consists of {1} consecutive {2}";
var newString:String = StringUtil.substitute(str, myObject, myNumber, myString);
substitute() should automatically cast its arguments to String, but I'm not sure if, as in your code, you can cast a MovieClip (myObject) as a String.
Another good option, especially if you've used printf in other programming languages, is this third-party printf-as3 function.

Casting objects to strings
The method toString() is defined on the Object class. So all objects have this method defined for them. Calling myObject.toString() will therefore usually give you what you're looking for. Certain objects define additional methods, such as date.getHours(), which return string descriptions of the object in a different format from that supplied by getString().
For native types such as int, you can cast using String(myInt).
Concatenating strings together
You can then add together the different parts of a string as follows:
var myString:String = "There are " + String(24) + " hours in a day."
Hope that helps,
Dave

The shorter way I'd do it is something like:
var finalString:String = "One " + myObject + " consists of " + myNumber + " " + myString;
A single or double quote initiates a string literal. If you use the + symbol to append something to a string literal it's going to automatically call toString() on that object.
myObject will return [Object MovieClip], though. What you want to do is create a custom class that extends MovieClip and then override the toString() protected method to return whatever string you want it to spit out.
Hope that helps!

Related

Xstream ConversionException for Wrong Input String

I'm using xStream to some JSON. I've used xstream quite extensively over the years. However this issue has me stumped.
I'm getting the following ConversionException...
com.thoughtworks.xstream.converters.ConversionException: For input string: ".232017E.232017E44"
---- Debugging information ----
message : For input string: ".232017E.232017E44"
cause-exception : java.lang.NumberFormatException
cause-message : For input string: ".232017E.232017E44"
class : java.sql.Timestamp
required-type : java.sql.Timestamp
converter-type : com.etepstudios.xstream.XStreamTimestampConverter
line number : -1
class[1] : com.pbp.bookacall.dataobjects.AppleReceipt
converter-type[1] : com.thoughtworks.xstream.converters.reflection.ReflectionConverter
class[2] : com.pbp.bookacall.dataobjects.AppleReceiptCollection
version : 1.4.10
-------------------------------
at com.etepstudios.xstream.XStreamTimestampConverter.unmarshal(XStreamTimestampConverter.java:87)
In my XStreamTimestampConverter class I print out the value that is attempting to be converted.. Which turns out to be the following...
XStreamTimestampConverter value = 2017-08-05 23:44:23.GMT
Here is the unmarshal function in my converter...
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context)
{
Timestamp theTimestamp;
Date theDate;
String value = reader.getValue ();
try
{
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.Z");
theDate = formatter.parse(value);
theTimestamp = new Timestamp (theDate.getTime());
}
catch (Exception e)
{
System.out.println ("XStreamTimestampConverter value = " + value);
throw new ConversionException(e.getMessage(), e);
}
return theTimestamp;
}
Any idea where this odd string is coming from? It does not exist anywhere in my JSON. Does xstream have some odd .[num]E.[num]E[num] notation for something? These numbers can change as I run this each time. Also I get an For input string: "" on occasion too. Yet the value is similar to the what is above. It's like it's randomly getting odd values for somewhere.
The data source is from Apple's In-App Purchase /VerifyReceipt web call. The system works just fine some times but then others it does not. It's also important to note that in this very case it parsed 100s of other Date/Timestamp strings using this converter. It just get's confused. Perhaps due to the size of the data?
So I figured out what was going on here. The unmarshal function above is not exactly as I have it in code...
The SimpleDateFormat formatter is actually set in the class rather than in the unmarshal method. Therefore if Xstream holds on to an instance of my converter and the unmarshal is called across multiple threads then it is possible that the formatter can get confused since it is the same object.
That's my only guess at this point as moving the formatter initialization into the method solved the issue. I would say SimpleDateFormatter is not thread safe?
It was the just the sheer about of data and the number of times it was concurrently being called that exposed this issue. Just a tip for anyone else in case this happens to them.

as3 array string to movieClip

UPDATED ANSWERED: Found a WORK AROUND. I changed my array to an array of mc names rather than strings so it works now. Nevertheless, curious if this question could be answered.
I have a random array of string names. I sort them out randomly. But I need to add
an existing movieclip based the string name.
Here is what I have that doesn't work.
public function addToStage()
{
Happy = sorted.sort( randomize );
trace("First is: " +Happy[0]); /// Works!
addChild(Happy[0]); // Does not work
}
ERROR I GET
TypeError: Error #1034: Type Coercion failed: cannot convert "Apple" to flash.display.DisplayObject.
at Main/addToStage()[C:etc..\Main.as:74]
at Main/init()[C: etc...\Main.as:64]
at Main()[C: etc... \Main.as:25]
Assuming the strings in the array are names of MovieClip vars then it's as simple as this:
this.addChild(this[Happy[0]])
That effectively looks for a variable with the name equivalent to the string in the array, so this["hello"] will look for this.hello
If the strings aren't names of movieclip vars then you would need to "map" the movieclips to the strings using a dictionary or Object like so:
var dictionary:Dictionary = new Dictionary;
dictionary["hello"] = movieClipVariable;
this.addChild(dictionary["hello"]);
Let me know if this isn't what you meant and I'll have another look
Arrays are dynamic, types are lost on children of the array, the are stored as Object.
to add the to the display list you must cast them to a DisplayObject or class that extends a DisplayObject:
addChild(Happy[0] as DisplayObject); // should work
you have to do type cast a from object to displayobject.
private var tempMC:MovieClip;
public function addToStage()
{
Happy = sorted.sort( randomize );
trace("First is: " +Happy[0]); /// Works!
tempMC = Happy[0] as MovieClip;
addChild(tempMC); // it should work
}

AS3: Is it possible to create a variable to hold instance name?

I am trying to have a more dynamic function and would like to allow the functions instance name were it outputs the text to be changeable.
for example
function example_function(url,instance_name){
instance_name.text = url;
}
example_function('www.example.com','url_txt');
example_function('www.another.com','more_txt');
Is this possible?
Yes, just parse the string into square brackets next to the instance's owner. For example:
this[instance_name].text = url;
More info:
Take this object:
var obj:Object = {
property1: 10,
property2: "hello"
};
Its properties can be accessed either as you'd expect:
obj.property1;
obj.property2;
Or as mentioned above:
obj["property1"];
obj["property2"];
I suggest using a function like this one I've created to tighten your code up a bit:
function selectProperty(owner:*, property:String):*
{
if(owner.hasOwnProperty(property)) return owner[property];
else throw new Error(owner + " does not have a property \"" + property + "\".");
return null;
}
trace(selectProperty(stage, "x")); // 0
trace(selectProperty(stage, "test")); // error
It is definitely possible, but it's not really best practice to do it with Strings like that. Instead, you can pass in a reference to the variable you're trying to modify.
function example_function(url : String, instance : TextField) : void {
instance.text = url;
}
example_function("www.example.com", url_txt);
This gives you strong typing so you can tell at compile time if you're operating on a TextField or not. If you aren't, you'll get an error because the 'text' property doesn't exist. You'll be able to find and track down errors faster this way.
However, if you must do it with Strings, you can access any property on any object using a string key like:
var myInstance = this[instance_name]
So in your example, you could do:
function example_function(url : String, instance : TextField) : void {
this[instance_name].text = url;
}
example_function("www.example.com", "url_txt");

AS3 - Returning a property of a class rather than the class itself

In ActionScript 3, there are some classes that will represent a value rather than the class itself. It's hard to explain properly what I mean, so take this example:
var str:String = "something";
var mc:MovieClip = new MovieClip();
trace(str); // something
trace(mc); // [object MovieClip]
You'll notice that the first trace outputs a value, rather than [object String]. Ontop of this, I can still make use of methods of String, like this:
var ar:Array = str.split('s');
Even though in a way you could almost read the above as:
"something".split('s');
I have a class AvLevelData that has some methods that deal with level data (which is essentially a String). At the moment there is a property data:String which represents the core level data.
The question I have is - can I replicate the behaviour of String in that when I trace or assign an instance of AvLevelData, the result is actually the String data.
For example, at the moment I need to go:
var levelData:AvLevelData = new AvLevelData();
trace(levelData.data);
To get the data. I instead want to be able to simply do the following:
var levelData:AvLevelData = new AvLevelData();
trace(levelData); // some level data string
Is this possible?
If you wan't your object to trace out your own fabricated string then you must implement a toString() function on your AvLevelData class.
In your example above, the MovieClip trace outputs: [Object MovieClip]; this comes from the default toString() implementation for Object (found on Object.prototype) . Note, you cannot override toString() as it only exists on the prototype of Object (remnants of the AS2/Javascript world), all you need to do is provide your own implementation with the same name. For instance:
public function toString():String {
return "MyCustomObjectString";
}
Some of the most basic types - String, int, Number, uint, Boolean, to name a few - are not classes / objects per se, they are primitives. In some languages there is a wrapper class available for some of these so they can be treated like objects, though Flash doesn't do this so much from my experience.
Probably the best way to answer your question is to make a toString() method for your AvLevelData class:
public function toString():String {
return data;
}
Any time you treat a class as a string (such as by putting it in trace()), flash (and many other languages) try to call toString() on the object. Typically this results in a string that's not helpful. But if you define your own toString() method, you can control what string gets output.
Another option is to simply do:
trace(AvLevelData.data);
Since that variable is a string, it should trace just fine.

Typed AS3 JSON Encoder and Decoder?

I need to encode and Decode AS3 Objects in a typed manner. http://code.google.com/p/as3corelib/ only supports untyped encoding and decoding.
http://code.google.com/p/ason/ supports some kind of typed objects but is not very robust, e.g. it fails on Date Objects. Any Recommendations ?
To make it clear: It MUST be JSON and it MUST be strong typed and robust.
JSON is built in in AS3. The preferred method to transmit data over the wire is AMF, which does provide you typed objects.
If you have to use JSON, then I guess that you might have to do with some sort of custom protocol to be able encode/decode with types.
You would actually need a reflection utility that read beans in JSON format and then produce your object. It really depends on how deep you want to go.
as3Commons has a reflect package that could help. They also have a JSONTypeProvider, which is not exactly what you need but can put you in the right tract.
You could modify any of the IOC frameworks to produce the context by parsing JSON instead of the regular XML most of them use.
You could modify ASON and add a custom type parser. You would have to send a variable in your JSON object containing the type of the object. And use that in with flash.utils.getDefinitionByName.
Another approach would be to just parse the objects with a regular JSON parser and then if it has a defined type create an instance of that objet, and initialize the properties.
Something like this, to get you started:
var beanInfo:Object = JSON.decode( jsonString );
beanInfo = _parseBean( beanInfo );
private function _parseBean(beanInfo:Object):Object{
if ( beanInfo.hasOwnProperty("_type") ) {
var clazz:Class = getDefinitionByName( beanInfo._type ) as Class;
beanInfo.__clazz = clazz;
var instance:Object = new clazz;
for( var prop:String in beanInfo ) {
if( instance.hasOwnProperty(prop) ) target[prop] = _getPropertyFrom(beanInfo[prop]);
}
}
}
private function _getPropertyFrom(property:String):* {
var xml:XML = describeType( beanInfo.__clazz );
//find the type of the current property.
var type:String = xml...
//if is a simple object then do something like
switch( type ) {
case "number":
return parseFloat(property ) as Number;
break;
case "int":
case "uint":
return parseInt( property );
break;
case "string":
return property as String;
break;
...
default
//As it is it does not suppor complex objects.
//You would use reflection. But then you could save the whole switch...
break;
}
}
Flash has its own serialization system.
var serializer:ByteArray = new ByteArray();
serializer.writeObject(new Sprite());
serializer.position = 0;
var data:String = serializer.readUTFBytes(serializer.bytesAvailable);
trace(data); //Will show you the binary jibberish
You can use registerClassAlias to add support for custom classes.
JSON doens't really define a means to convey type information. It's just strings and ints and arrays and so on. So basically you need some sort of "pickle" for AS3 that's based on JSON. I would suggest you look into Flex/Flash remoting solutions and see how they package objects to be transmitted for RPC; you might be able to modify that solution to use JSON. I'm actually doubtful you'll find a library like this. Does it have to be JSON? I'm pretty sure there are XML based libraries that do this.
JSON is not implemented in the flash virtual machine, and therefore there is no typed object "JSON" as there is "Xml." So basically you can decode JSON just fine, but the type you're going to get is Object. You can them access data using the key in the object as an associative array.
http://blog.alien109.com/2009/02/11/php5-json-as3corelib-a-beautiful-thing/
JSON lib/utils official from adobe:
http://code.google.com/p/as3corelib/source/browse/#svn%2Ftrunk%2Fsrc%2Fcom%2Fadobe%2Fserialization%2Fjson
As good as it gets. :)
There are two operations you need to consider: 1) serializing an object of a particular type into JSON and 2) deserializing a JSON string into an object of a particular type.
The serialization part is easy - just use the built-in JSON.stringify(). Deserializing a JSON string into an object of a particular type in ActionScript is where it gets interesting (and where the answer to your question is). You need to write your own deserialization function for the classe(s) you will need to deserialize. In that function, you need to provide a reviver function to JSON.parse(), which allows you to customize how the JSON gets deserialized.
For example:
public static function deserializeComplexObject(json:String):ComplexObject
{
if (null == json || "null" == json)
{
return null;
}
var complexObject:ComplexObject = new ComplexObject();
var parsedObject:Object = JSON.parse(
json,
function (key:String, value:Object):*
{
switch (key)
{
case “keyForNumber”:
return value;
case “keyForComplexObject2”:
return deserializeComplexObject2(JSON.stringify(value));
case “keyForComplexObject3”:
return deserializeComplexObject3(JSON.stringify(value));
case “keyForString”:
return value;
case “keyForBoolean”:
return value;
default:
return value;
}
}
);
complexObject.keyForNumber = parsedObject.keyForNumber;
complexObject.keyForComplexObject2 = parsedObject.keyForComplexObject2;
// continue setting fields
// …
return complexObject;
}
Each case statement corresponds to a top-level key in the JSON string. You don't actually need separate case statements for every key - you can use the default case to handle all keys that map to values that are one of the simple types (Object, Array, String, Number, Boolean, null) by returning the value as-is.
I have now forked the json part of http://code.google.com/p/as3corelib/ and added typed object support...