Skip to content

Instantly share code, notes, and snippets.

@adamflorin
Created July 16, 2013 05:11
Show Gist options
  • Save adamflorin/6005949 to your computer and use it in GitHub Desktop.
Save adamflorin/6005949 to your computer and use it in GitHub Desktop.
De/serialize JS objects to and from Dicts, respecting arrays, nulls, etc.
{exec} = require 'child_process'
task "build", "Compile all CoffeeScript to JavaScript", ->
exec 'coffee --bare --compile --join persistence-test.js ' +
("#{file}.coffee" for file in ["utilities", "persistence", "test"]).join(" "),
(err, stdout, stderr) ->
throw err if err
console.log stdout + stderr
// Generated by CoffeeScript 1.6.2
var Persistence, bang, objectType, roundtrip;
objectType = function(object) {
return Object.prototype.toString.call(object).match(/(\S+)]$/)[1];
};
Persistence = (function() {
function Persistence() {}
Persistence.prototype.serializeObject = function(object) {
var dict, key, value;
dict = new Dict;
for (key in object) {
value = object[key];
if (value != null) {
dict.set(key, !(value instanceof Object) ? value : this.serializeObject(value));
}
}
return dict;
};
Persistence.prototype.deserializeObject = function(dict) {
var isArray, key, keys, lastKey, object, value, _i, _len, _results;
keys = this.normalizedKeys(dict);
if (keys.length === 0) {
return null;
}
object = new Object;
isArray = true;
lastKey = "-1";
for (_i = 0, _len = keys.length; _i < _len; _i++) {
key = keys[_i];
if (!(key != null)) {
continue;
}
value = dict.get(key);
if (objectType(value) === "Dict") {
value = this.deserializeObject(value);
}
object[key] = value;
if (isArray) {
if (parseInt(key) !== parseInt(lastKey) + 1) {
isArray = false;
}
lastKey = key;
}
}
if (!isArray) {
return object;
} else {
_results = [];
for (key in object) {
value = object[key];
_results.push(value);
}
return _results;
}
};
Persistence.prototype.normalizedKeys = function(dict) {
var keys;
keys = dict.getkeys();
if (keys == null) {
keys = [];
}
if (!(keys instanceof Array)) {
keys = [keys];
}
return keys;
};
return Persistence;
})();
bang = function() {
post(JSON.stringify(roundtrip({
just: "another",
normal: "object"
})));
return post(JSON.stringify(roundtrip(["an", "array"])));
};
roundtrip = function(object) {
return Persistence.prototype.deserializeObject(Persistence.prototype.serializeObject(object));
};
{
"patcher" : {
"fileversion" : 1,
"appversion" : {
"major" : 6,
"minor" : 1,
"revision" : 2,
"architecture" : "x86"
}
,
"rect" : [ 85.0, 69.0, 226.0, 128.0 ],
"bglocked" : 0,
"openinpresentation" : 0,
"default_fontsize" : 12.0,
"default_fontface" : 0,
"default_fontname" : "Arial",
"gridonopen" : 0,
"gridsize" : [ 15.0, 15.0 ],
"gridsnaponopen" : 0,
"statusbarvisible" : 2,
"toolbarvisible" : 1,
"boxanimatetime" : 200,
"imprint" : 0,
"enablehscroll" : 1,
"enablevscroll" : 1,
"devicewidth" : 0.0,
"description" : "",
"digest" : "",
"tags" : "",
"boxes" : [ {
"box" : {
"id" : "obj-3",
"maxclass" : "button",
"numinlets" : 1,
"numoutlets" : 1,
"outlettype" : [ "bang" ],
"patching_rect" : [ 30.0, 30.0, 20.0, 20.0 ]
}
}
, {
"box" : {
"fontname" : "Arial",
"fontsize" : 12.0,
"id" : "obj-1",
"maxclass" : "newobj",
"numinlets" : 1,
"numoutlets" : 1,
"outlettype" : [ "" ],
"patching_rect" : [ 30.0, 60.0, 119.0, 20.0 ],
"saved_object_attributes" : {
"filename" : "persistence-test.js",
"parameter_enable" : 0
}
,
"text" : "js persistence-test.js"
}
}
],
"lines" : [ {
"patchline" : {
"destination" : [ "obj-1", 0 ],
"disabled" : 0,
"hidden" : 0,
"source" : [ "obj-3", 0 ]
}
}
],
"dependency_cache" : [ {
"name" : "persistence-test.js",
"bootpath" : "/Users/adam/projects/loom/dev/json-to-dict",
"patcherrelativepath" : ".",
"type" : "TEXT",
"implicit" : 1
}
]
}
}
#
# De/serialize JS objects to and from Dicts.
#
# Copyright 2013 Adam Florin
#
class Persistence
# Utility: Feed Object into Dict structure, recursively.
#
# Don't store null values.
#
serializeObject: (object) ->
dict = new Dict
for key, value of object when value?
dict.set(
key,
unless value instanceof Object then value else @serializeObject value)
return dict
# Utility: Build Object from Dict structure, recursively.
#
# Determine that dict should be built as an Array if all of its keys are
# contiguous numbers beginning with "0".
#
# If dict is empty, there's no way to determine if it's supposed to be an
# array or object, so just return null so object can initialize that value.
#
deserializeObject: (dict) ->
keys = @normalizedKeys dict
return null if keys.length == 0
object = new Object
isArray = true
lastKey = "-1"
for key in keys when key?
value = dict.get(key)
value = @deserializeObject(value) if objectType(value) is "Dict"
object[key] = value
if isArray
isArray = false if parseInt(key) isnt parseInt(lastKey) + 1
lastKey = key
return unless isArray then object else (value for key, value of object)
# The return value of getkeys() is unreliable, presumably because it was
# designed for use in Max, not in JS. Return an array here, no matter
# whether getkeys() returned an array, a single value, or null.
#
normalizedKeys: (dict) ->
keys = dict.getkeys()
keys = [] unless keys?
keys = [keys] unless keys instanceof Array
return keys
#
# test.coffee: expose functionality to Max for testing.
#
# Run a few tests on bang.
#
# Check the console for output
#
bang = ->
post JSON.stringify roundtrip
just: "another"
normal: "object"
post JSON.stringify roundtrip ["an", "array"]
# Serialize a JS object to a Dict, then immediately deserialize it.
#
roundtrip = (object) ->
Persistence::deserializeObject Persistence::serializeObject object
# Get string representation of object type, but more precise than typeof.
#
objectType = (object) ->
Object::toString.call(object).match(/(\S+)]$/)[1]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment