Typed AS3 JSON Encoder and Decoder? - actionscript-3

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...

Related

ServiceStack.Text CSV serialization of IEnumerable<object> ignores custom serialization functions

Firstly, please forgive any rookie mistakes here - I'm not a regular poster I'm afraid.
Now on to the nitty gritty...
I am trying to use ServiceStack.Text to serialize objects to CSV. If I keep it simple, everything works as expected when serializing objects of a known type.
However I want to serialize many objects and I don't know the type at runtime so I am writing a reusable component where all data is treated as a System.Object. We already do this same routine for Json serialization without problems. But CsvSerializer appears to handle objects differently during serialization.
Sample code
public void TestIEnumerableObjectSerialization()
{
var data = GenerateSampleData();
JsConfig<DateTime>.SerializeFn =
time => new DateTime(time.Ticks, DateTimeKind.Utc).ToString("yyyy-MM-dd HH:mm:ss");
var csv = CsvSerializer.SerializeToCsv(data);
Console.WriteLine(csv);
Assert.Equal("DateTime\r\n"
+ "2017-06-14 00:00:00\r\n"
+ "2017-01-31 01:23:45\r\n",
csv);
}
object[] GenerateSampleData()
{
return new object[] {
new POCO
{
DateTime = new DateTime(2017,6,14)
},
new POCO
{
DateTime = new DateTime(2017,1,31, 01, 23, 45)
}
};
}
public class POCO
{
public DateTime DateTime { get; set; }
}
The result of this code is that the custom serialization function is not invoked, and the DateTime is written out using the standard ToString() method.
The cause?
The CsvWriter.Write method is inspecting the type of the records and if the type is Object it is treated as a Dictionary<string, object> and CsvDictionaryWriter generates the output.
In turn, CsvDictionaryWriter uses the ToCsvField() extension method to write each property a record.
The problem is that ToCsvField() converts the value of each property to a string using ToString() meaning no custom serialization is performed.
JsonSerializer uses TypeSerializer.SerializeToString(text) to serialize the properties of an Object using any configured custom serialization functions; but this doesn't happen with CsvSerializer.
A possible solution?
Without complicating CsvSerializer, the ToCsvField() extension method could be updated to use TypeSerializer to handle the serialization to a string. Here is what I've been testing with so far:
public static object ToCsvField(this object text)
{
var textSerialized = TypeSerializer.SerializeToString(text).StripQuotes();
return textSerialized == null || !CsvWriter.HasAnyEscapeChars(textSerialized)
? textSerialized
: string.Concat
(
CsvConfig.ItemDelimiterString,
textSerialized.Replace(CsvConfig.ItemDelimiterString, CsvConfig.EscapedItemDelimiterString),
CsvConfig.ItemDelimiterString
);
}
So far I haven't come across an issue with this change, although someone may prefer not to allocate a new intermediate variable before the return statement.
Hopefully that is enough information, so on to my questions...
Has anyone else experienced this issue?
Am I doing something wrong and should I be serializing Objects a different way?
If this is a suitable fix/implementation of TypeSerializer, what are the chances of this being addressed in an update to ServiceStack.Text? I would raise an issue on GitHub but the ServiceStack.Text repo doesn't let me raise issues.
Thanks in advance.

Getting all of the variables of a dart class for json encoding

I have a class
class Person{
String _fn, _ln;
Person(this._fn, this._ln);
}
Is there a way to get a list of variables and then serialize it? Essentially i want to make a toJson, but i wanted to have it generic enough such that key is the variable name, and the value is the value of the variable name.
In javascript it would be something like:
var myObject = {}; //.... whatever you want to define it as..
var toJson = function(){
var list = Object.keys(myObject);
var json = {};
for ( var key in list ){
json[list[key]] = myObject[list[key]] ;
}
return JSON.stringify(json);
}
Dart doesn't have a built in functionality for serialization. There are several packages with different strategies available at pub.dartlang.org. Some use mirrors, which is harmful for client applications because it results in big or huge JS output size. The new reflectable packages replaces mirrors without the disadvantage but I don't know if serialization packages are already ported to use it instead. There are also packages that use code generation.
There is a question with an answer that lists available solutions. I'll look it up when I'm back.

Can I automatically serialize a Dart object to send over a Web Socket?

I just saw that there are some libraries for running a Dart web server, like Start.
So I was thinking something like this..
If both client and server code is written in Dart, is it possible to send "Dart objects" via websockets (or normal REST for that matter) so that the type information remains on the other end? Or do I need to serialize/deserialize via JSON or something similar on the way? Or am I over thinking things here?
regards Oskar
You will need to serialize the Dart object somehow. You can try JSON, or you can try the heavy-duty serialization package.
There is no fully automatic JSON serialization for custom Dart classes. You will need to add a custom toJson serializer and create some sort of fromJson constructor.
e.g. if you had a Person class, you could do something like this:
import 'dart:json' as json;
class Person {
String name;
int age;
Person(this.name, this.age);
Person.fromJson(String json) {
Map data = json.parse(json);
name = data['name'];
age = data['age'];
}
Map toJson() {
return {'name': name, 'age': age};
}
}
Note: the fromJson is just a convention. You will need to call it somehow, there is no built-in mechanism to take an arbitrary JSON string and call the right constructors on your custom object.
As mentioned above, the serialization package is more heavy weight, but much more full featured. Here is an example from its docs:
// uses the serialization package
var address = new Address();
address.street = 'N 34th';
address.city = 'Seattle';
var serialization = new Serialization()
..addRuleFor(address);
Map output = serialization.write(address);
and
// uses serialization
var serialization = new Serialization()
..addRuleFor(address,
constructor: "create",
constructorFields: ["number", "street"],
fields: ["city"]);
You can use the 'exportable' package to render your class to JSON or a map in a more declarative fashion.
import 'package:exportable/exportable.dart';
class Product extends Object with Exportable
{
#export String ProductName;
#export num UnitPrice;
#export bool Discontinued;
#export num UnitsInStock;
Product(this.ProductName, this.UnitPrice, this.Discontinued, this.UnitsInStock);
}
Product prod = new Product("First", 1.0, false, 3 );
var json = prod.toJson(); // {"ProductName":"First","UnitPrice":1.0,"Discontinued":false,"UnitsInStock":3}

Groovy JsonBuilder: object properties being serialized in random order

I'm currently having a problem with the groovy JsonBuilder: the properties of the objects I am trying to serialize are coming out in seemingly random order.
Here's the objects' class:
class Game {
String title
String gameImg2
String description
}
And this is the code I've been using:
def game = new Game(title: "a game", gameImg2: "an image", description: "desc")
def json = new JsonBuilder(game)
From this I would expect the output to be:
{"title":"a game","gameImg2":"an image", "description":"desc"}
but instead, I'm getting:
{"gameImg2":"gameImg","title":"hello","description":"desc"}.
From looking at the JsonBuilder example code, it seems that order should be maintained, and indeed, it looks like the toJson method iterates over object.properties, which is a LinkedHashMap. I would have thought this would go through the properties in the order they are declared in the class.
My best guess is that this is something to do with my initialisation of the game object - could using Map syntax to assign properties somehow, but again, this seems to create a LinkedHashMap which should preserve order.
Does anyone have an inkling of what I could have done wrong or incorrectly assumed here? Any leads would be a big help!
The problem is that you are serializing an Object to JSON. The object has declared fields that are translated to Java.
In Java it is not (easily) possible to traverse fields in the order of declaration and the JsonBuilder code definitely makes no attempt to allow this behavior.
Looking at the source code for the JsonBuilder one can see that it uses a class called JsonOutput.groovy to serialize the Object you are passing it, like so:
static String toJson(object) {
if (object == null) {
"null"
} else if (object instanceof Collection ||
object.class.isArray() ||
object instanceof Iterator ||
object instanceof Enumeration) {
"[" + object.collect { toJson(it) }.join(',') + "]"
} else if (object instanceof Enum) {
'"' + object.name() + '"'
} else {
def properties = object.properties
properties.remove('class')
properties.remove('declaringClass')
properties.remove('metaClass')
toJson(properties)
}
}
As you can see the code calls the properies member of the object which returns the object members, the order of the fields depends on the JVM and not the order of declaration.
If you want to maintain order you will need to either pass in a map representing the object or build the JSON object in order manually.
The other alternative is writing your own custom builder, which is much more complicated...

Is it possible to get all member variables in flash(AS3)?

I am trying grab all the member variables in AS3, and then foreach one i would like to process it in various ways. I would need the name and then if it is a collection of some type I would like to loop through that collection as well. I am attempting to essentially serialize in a somewhat custom fashion.
Thanks!
If you're looking to serialize an object, you will definitely want to use JSON.
JSON basically converts objects into strings and also the other way round using an encode()/serialize() and decode()/deserialize() function.
There is a built-in JSON class in AS3, and it's really easy to use.
Once you do something like:
var myObject:Object = {};
var myObjectString:String = JSON.serialize(myObject);
After getting the string, you can do all your switch logic to manipulate each of your different variables and convert it back into an object via the deserialize() function.
You could use describeType. That returns information about the object as XML. By default, you can iterate over public properties in objects. You could try something like...
// the object to iterate over
var someObj:Object = {};
for(var prop:String in someObj) {
// check to see if its something you want to iterate over
if (someObj[prop] is Array) {
// iterator over the property here
}
}
I hope this answers your question.