I ActionScript 3.0 finns ett systemspecifikt API för kodning och avkodning av ActionScript-objekt med hjälp av JSON-formatet (JavaScript Object Notation). JSON-klassen och de medlemsfunktioner som stöds uppfyller med några få undantag den femte upplagespecifikationen av ECMA-262.
Adobe-experten Todd gör en jämförelse mellan ett systemspecifikt JSON API och JSON-klassen as3corelib från tredje part. Se
Working with Native JSON in Flash Player 11
.
Översikt av JSON-API
ActionScript JSON API består av JSON-klassen och medlemsfunktioner för
toJSON()
i fem systemspecifika klasser. För program som kräver en egen JSON-kodning för någon klass, ger ActionScript-ramverket möjlighet att åsidosätta standardkodningen.
Klassen JSON hanterar internt import och export för alla ActionScript-klasser som inte har en
toJSON()
-medlem. I sådana fall går JSON igenom de publika egenskaperna för varje objekt som upptäcks. Om ett objekt innehåller andra objekt, repeteras JSON för de kapslade objekten och utför samma genomgång. Om något av objekten tillhandahåller en
toJSON()
-metod, kommer den anpassade metoden för JSON att användas i stället för dess interna algoritm.
JSON-gränssnittet består av en kodningsmetod,
stringify()
, och en avkodningsmetod,
parse()
. Dessa metoder ger en parameter som gör att du kan infoga egen logik i arbetsflödet för JSON-kodning/-avkodning. För
stringify()
heter parametern
replacer
och för
parse()
reviver
. Dessa parametrar tar emot en funktionsdefinition med två argument med hjälp av följande signatur:
function(k, v):*
toJSON()-metoder
Signaturen för
toJSON()
-metoderna är
public function toJSON(k:String):*
JSON.stringify()
anropar
toJSON()
, om den finns, för varje publik egenskap som upptäcks under genomgången av ett objekt. Egenskapen består av ett nyckelvärdepar. När
stringify(
) anropar
toJSON()
, skickas nyckeln,
k
, för egenskapen som för närvarande undersöks. I en vanlig
toJSON()
-implementation utvärderas varje egenskapsnamn och returnerar den önskade kodningen av värdet.
Metoden
toJSON()
kan returnera alla typer av värden (anges med *), inte bara en sträng. Den här variabla returtypen tillåter
toJSON()
att returnera ett objekt om det skulle behövas. Om till exempel en egenskap för din klass innehåller ett objekt från ett annat tredjepartsbibliotek, kan du returnera objektet när
toJSON()
upptäcker egenskapen. JSON kommer sedan att repeteras i tredjepartsobjeket. Kodningsprocessen ser ut så här:
-
Om
toJSON()
returnerar ett objekt som inte utvärderas som en sträng, repeteras
stringify()
i detta objekt.
-
Om
toJSON()
returnerar en sträng för
stringify()
över det värdet till en annan sträng, returnerar den överförda strängen och fortsätter sedan till nästa värde.
I många fall är det bättre att ett objekt returneras än att en JSON-sträng som skapats av ditt program returneras. Returnerandet av ett objekt utnyttjar den inbyggda JSON-kodningsalgoritmen och gör det även möjligt för JSON att gå igenom kapslade objekt rekursivt.
Metoden
toJSON()
är inte definierad i klassen Object och inte heller i de flesta andra inbyggda klasser. Dess frånvaro leder till att JSON utför standardgenomgången av objektets publika egenskaper. Om du vill kan du även använda
toJSON()
för att visa objektets privata egenskaper.
Vissa inbyggda klasser kan medföra problem som ActionScript-biblioteken inte kan lösa effektivt för alla tänkbara användningssätt. För dessa klasser erbjuder ActionScript en enkel implementation som kan återimplementeras på klienter för att passa för de behov som finns. Klasserna som tillhandahåller enkla
toJSON()
-medlemmar är:
-
ByteArray
-
Date
-
Dictionary
-
XML
Du kan göra en underklass av ByteArray-klassen för att åsidosätta dess
toJSON()
-metod eller så kan du omdefiniera dess prototyp. Klasserna Date och XML, som deklareras sist, kräver att du använder klassprototypen för att omdefiniera
toJSON()
. Klassen Dictionary deklareras dynamiskt, vilket ger dig extra frihet att åsidosätta
toJSON()
.
Definiera anpassade JSON-beteenden
Om du vill implementera egen JSON-kodning och avkodning för inbyggda klasser kan du välja mellan flera alternativ. Du kan:
-
definiera eller åsidosätta
toJSON()
för din anpassade underklass för en icke-slutlig inbyggd klass,
-
definiera eller definiera om
toJSON()
för klassens prototyp,
-
definiera en
toJSON
-egenskap för en dynamisk klass,
-
använda parametrarna
JSON.stringify() replacer
och
JSON.parser() reviver
.
Definiera toJSON() på prototypen för en inbyggd klass
Den systemspecifika JSON-implementeringen i ActionScript speglar ECMAScript JSON-förhållandet definierad i ECMA-262, femte upplagan. Efterson det inte finns stöd för klasser i ECMAScript, definieras JSON-beteendet i ActionScript utifrån prototypbaserade skickningar. Prototyper är föregångare till ActionScript 3.0-klasser, som tillåter simulerade arv, tillägg av medlemmar och omdefinitioner.
ActionScript tillåter att du definierar eller omdefinierar
toJSON()
på prototypen för valfri klass. Denna behörighet gäller även för klasser som är märkta som slutgiltiga. När du definierar
toJSON()
för en klassprototyp, kommer definitionen att gälla för alla instanser av klassen inom programmets räckvidd. Här visas till exempel hur du kan definiera en
toJSON()
-metod för prototypen MovieClip:
MovieClip.prototype.toJSON = function(k):* {
trace("prototype.toJSON() called.");
return "toJSON";
}
När programmet sedan anropar
stringify()
i en MovieClip-instans, returnerar
stringify()
utdata för din
toJSON()
-metod:
var mc:MovieClip = new MovieClip();
var js:String = JSON.stringify(mc); //"prototype toJSON() called."
trace("js: " + js); //"js: toJSON"
Du kan också åsidosätta
toJSON()
i inbyggda klasser som definierar metoden. I följande kod åsidosätts till exempel
Date.toJSON()
:
Date.prototype.toJSON = function (k):* {
return "any date format you like via toJSON: "+
"this.time:"+this.time + " this.hours:"+this.hours;
}
var dt:Date = new Date();
trace(JSON.stringify(dt));
// "any date format you like via toJSON: this.time:1317244361947 this.hours:14"
Definiera eller åsidosätta toJSON() på klassnivå
Program behöver inte alltid använda prototyper för att omdefiniera
toJSON()
. Du kan också definiera
toJSON()
som en medlem i en underklass, om den överordnade klassen inte har markerats som slutlig. Du kan till exempel utöka klassen ByteArray och definiera en offentlig
toJSON()
-funktion:
package {
import flash.utils.ByteArray;
public class MyByteArray extends ByteArray
{
public function MyByteArray() {
}
public function toJSON(s:String):*
{
return "MyByteArray";
}
}
}
var ba:ByteArray = new ByteArray();
trace(JSON.stringify(ba)); //"ByteArray"
var mba:MyByteArray = new MyByteArray(); //"MyByteArray"
trace(JSON.stringify(mba)); //"MyByteArray"
Om en klass är dynamisk kan du lägga till en
toJSON
-egenskap till ett objekt i denna klass och tilldela en funktion till den enligt följande:
var d:Dictionary = new Dictionary();
trace(JSON.stringify((d))); // "Dictionary"
d.toJSON = function(){return {c : "toJSON override."};} // overrides existing function
trace(JSON.stringify((d))); // {"c":"toJSON override."}
Du kan åsidosätta, definiera eller definiera om
toJSON()
för alla ActionScript-klasser. De flesta inbyggda ActionScript-klasser definierar emellertid inte
toJSON()
. Klassen Object definierar inte
toJSON
i dess standardprototyp och deklarerar den inte som en klassmedlem. Endast en handfull inbyggda klasser definierar metoden som en prototypfunktion. Således kan du i de flesta klasser inte åsidosätta
toJSON()
på traditionellt sätt.
Inbyggda klasser som inte definierar
toJSON()
serialiseras till JSON via den interna JSON-implementeringen. Undvik om möjligt att ersätta denna inbyggda funktion. Om du definierar en
toJSON()
-medlem använder JSON-klassen din logik i stället för dess egna funktioner.
Använda parametern replacer för JSON.stringify()
Att åsidosätta
toJSON()
på en prototyp är användbart när du vill ändra en klass JSON-exportbeteende i hela programmet. I vissa klasser kanske din exportlogik endast gäller speciella fall under kortvariga villkor. För att tillgodose sådana små förändringar kan du använda parametern
replacer
i metoden
JSON.stringify()
.
Metoden
stringify()
tillämpar funktionen som skickas med
replacer
-parametern till objektet som kodas. Signaturen för denna funktion påminner om den som gäller för
toJSON()
:
function (k,v):*
Till skillnad från
toJSON()
, kräver funktionen
replacer
värdet,
v
, samt nyckeln,
k
. Den här skillnaden är nödvändig eftersom
stringify()
definieras för ett statiskt JSON-objekt i stället för att objektet kodas. När
JSON.stringify()
anropar
replacer(k,v
), kommer det att gå igenom det ursprungliga indataobjektet . Den implicita
this
-parametern, som skickas till funktionen
replacer
, refererar till objektet som håller nyckeln och värdet. Eftersom
JSON.stringify()
inte förändrar det ursprungliga indataobjektet, kommer detta objekt att förbli oförändrat i den behållare som gås igenom. Du kan således använda koden
this[k]
för att tillfråga nyckeln i originalobjektet. Parametern
v
innehåller värdet som konverteras med
toJSON()
.
Precis som
toJSON()
, kan funktionen
replacer
returnera olika typer av värden. Om
replacer
returnerar en sträng, kommer JSON-motorn att undertrycka innehållet med citattecken och omsluter sedan detta innehåll med ytterligare citattecken. Den här förpackningen garanterar att
stringify()
tar emot ett giltigt JSON-strängobjekt som behålls som en sträng i ett efterföljande anrop till
JSON.parse()
.
I följande kod används parametern
replacer
och den implicita
this
-parametern för att returnera värdena för
time
och
hours
i Date-objektet:
JSON.stringify(d, function (k,v):* {
return "any date format you like via replacer: "+
"holder[k].time:"+this[k].time + " holder[k].hours:"+this[k].hours;
});
Använda reviver-parametern i JSON.parse()
Parametern
reviver
i
JSON.parse()
-metoden gör det motsatta mot
replacer
-funktionen: Den konverterar en JSON-sträng till ett användbart ActionScript-objekt. Argumentet
reviver
är en funktion som tar två parametrar och returnerar valfri typ:
function (k,v):*
I denna funktion är
k
en nyckel och
v
värdet på
k
. Precis som
stringify()
, går
parse()
igenom nyckelvärdepar för JSON och tillämpar
reviver
-funktionen, om det finns någon, på varje par. Ett potentiellt problem är det faktum att JSON-klassen inte lämnar ut ett objekts ActionScript-klassnamn. Det kan således vara svårt att veta vilken typ av objekt som ska återställas. Detta problem kan vara speciellt besvärligt när objekten är kapslade. När du skapar
toJSON()
-,
replacer
- och
reviver
-funktioner, kan du göra egna identifiera för ActionScript-objekten som är exporterade, samtidigt som du behåller originalobjekten intakta.
Tolkningsexempel
I följande exempel visas en strategi för att återställa objekt tolkade från JSON-strängar. I det här exemplet definieras två klasser: JSONGenericDictExample och JSONDictionaryExtnExample. Klassen JSONGenericDictExample är en anpassad ordlisteklass. Varje post innehåller ett personnamn och födelsedatum samt ett unikt ID. Varje gång JSONGenericDictExample-konstruktorn anropas, lägger den till det nyligen skapade objektet i en intern statisk array med ett jämt ökande heltal som dess ID. Klassen JSONGenericDictExample definierar även en
revive()
-metod, som extraherar endast heltalsdelen från den längre
id
-medlemmen. Metoden
revive()
använder detta heltal för att söka efter och returnera rätt återställningsobjekt.
Klassen JSONDictionaryExtnExample utökar ActionScript-klassen Dictionary. Dess poster har ingen fast struktur, utan kan innehålla alla typer av data. Data tilldelas efter det att ett JSONDictionaryExtnExample-objekt har konstruerats, och inte som klassdefinierade egenskaper. JSONDictionaryExtnExample-poster använder JSONGenericDictExample-objekt som nycklar. När ett JSONDictionaryExtnExample-objekt återställs använder funktionen
JSONGenericDictExample.revive()
det ID som är associerat med JSONDictionaryExtnExample för att hämta korrekt nyckelobjekt.
Viktigast av allt är att metoden
JSONDictionaryExtnExample.toJSON()
returnerar en markörsträng, utöver JSONDictionaryExtnExample-objektet. Denna sträng identifierar att dessa JSON-utdata tillhör klassen JSONDictionaryExtnExample. Denna markör lämnar inget tvivel om vilken objekttyp som bearbetas med
JSON.parse()
.
package {
// Generic dictionary example:
public class JSONGenericDictExample {
static var revivableObjects = [];
static var nextId = 10000;
public var id;
public var dname:String;
public var birthday;
public function JSONGenericDictExample(name, birthday) {
revivableObjects[nextId] = this;
this.id = "id_class_JSONGenericDictExample_" + nextId;
this.dname = name;
this.birthday = birthday;
nextId++;
}
public function toString():String { return this.dname; }
public static function revive(id:String):JSONGenericDictExample {
var r:RegExp = /^id_class_JSONGenericDictExample_([0-9]*)$/;
var res = r.exec(id);
return JSONGenericDictExample.revivableObjects[res[1]];
}
}
}
package {
import flash.utils.Dictionary;
import flash.utils.ByteArray;
// For this extension of dictionary, we serialize the contents of the
// dictionary by using toJSON
public final class JSONDictionaryExtnExample extends Dictionary {
public function toJSON(k):* {
var contents = {};
for (var a in this) {
contents[a.id] = this[a];
}
// We also wrap the contents in an object so that we can
// identify it by looking for the marking property "class E"
// while in the midst of JSON.parse.
return {"class JSONDictionaryExtnExample": contents};
}
// This is just here for debugging and for illustration
public function toString():String {
var retval = "[JSONDictionaryExtnExample <";
var printed_any = false;
for (var k in this) {
retval += k.toString() + "=" +
"[e="+this[k].earnings +
",v="+this[k].violations + "], "
printed_any = true;
}
if (printed_any)
retval = retval.substring(0, retval.length-2);
retval += ">]"
return retval;
}
}
}
När följande skript anropar
JSON.parse()
för ett JSONDictionaryExtnExample-objekt anropar funktionen
reviver
JSONGenericDictExample.revive()
för alla objekt i JSONDictionaryExtnExample. Det här anropet extraherar det ID som representerar objektnyckeln. ID:t används sedan av funktionen
JSONGenericDictExample.revive()
för att hämta och returnera det lagrade JSONDictionaryExtnExample-objektet från en privat, statisk array.
import flash.display.MovieClip;
import flash.text.TextField;
var a_bob1:JSONGenericDictExample = new JSONGenericDictExample("Bob", new Date(Date.parse("01/02/1934")));
var a_bob2:JSONGenericDictExample = new JSONGenericDictExample("Bob", new Date(Date.parse("05/06/1978")));
var a_jen:JSONGenericDictExample = new JSONGenericDictExample("Jen", new Date(Date.parse("09/09/1999")));
var e = new JSONDictionaryExtnExample();
e[a_bob1] = {earnings: 40, violations: 2};
e[a_bob2] = {earnings: 10, violations: 1};
e[a_jen] = {earnings: 25, violations: 3};
trace("JSON.stringify(e): " + JSON.stringify(e)); // {"class JSONDictionaryExtnExample":
//{"id_class_JSONGenericDictExample_10001":
//{"earnings":10,"violations":1},
//"id_class_JSONGenericDictExample_10002":
//{"earnings":25,"violations":3},
//"id_class_JSONGenericDictExample_10000":
// {"earnings":40,"violations":2}}}
var e_result = JSON.stringify(e);
var e1 = new JSONDictionaryExtnExample();
var e2 = new JSONDictionaryExtnExample();
// It's somewhat easy to convert the string from JSON.stringify(e) back
// into a dictionary (turn it into an object via JSON.parse, then loop
// over that object's properties to construct a fresh dictionary).
//
// The harder exercise is to handle situations where the dictionaries
// themselves are nested in the object passed to JSON.stringify and
// thus does not occur at the topmost level of the resulting string.
//
// (For example: consider roundtripping something like
// var tricky_array = [e1, [[4, e2, 6]], {table:e3}]
// where e1, e2, e3 are all dictionaries. Furthermore, consider
// dictionaries that contain references to dictionaries.)
//
// This parsing (or at least some instances of it) can be done via
// JSON.parse, but it's not necessarily trivial. Careful consideration
// of how toJSON, replacer, and reviver can work together is
// necessary.
var e_roundtrip =
JSON.parse(e_result,
// This is a reviver that is focused on rebuilding JSONDictionaryExtnExample objects.
function (k, v) {
if ("class JSONDictionaryExtnExample" in v) { // special marker tag;
//see JSONDictionaryExtnExample.toJSON().
var e = new JSONDictionaryExtnExample();
var contents = v["class JSONDictionaryExtnExample"];
for (var i in contents) {
// Reviving JSONGenericDictExample objects from string
// identifiers is also special;
// see JSONGenericDictExample constructor and
// JSONGenericDictExample's revive() method.
e[JSONGenericDictExample.revive(i)] = contents[i];
}
return e;
} else {
return v;
}
});
trace("// == Here is an extended Dictionary that has been round-tripped ==");
trace("// == Note that we have revived Jen/Jan during the roundtrip. ==");
trace("e: " + e); //[JSONDictionaryExtnExample <Bob=[e=40,v=2], Bob=[e=10,v=1],
//Jen=[e=25,v=3]>]
trace("e_roundtrip: " + e_roundtrip); //[JSONDictionaryExtnExample <Bob=[e=40,v=2],
//Bob=[e=10,v=1], Jen=[e=25,v=3]>]
trace("Is e_roundtrip a JSONDictionaryExtnExample? " + (e_roundtrip is JSONDictionaryExtnExample)); //true
trace("Name change: Jen is now Jan");
a_jen.dname = "Jan"
trace("e: " + e); //[JSONDictionaryExtnExample <Bob=[e=40,v=2], Bob=[e=10,v=1],
//Jan=[e=25,v=3]>]
trace("e_roundtrip: " + e_roundtrip); //[JSONDictionaryExtnExample <Bob=[e=40,v=2],
//Bob=[e=10,v=1], Jan=[e=25,v=3]>]
|
|
|