JSON è un formato per l’interscambio dei dati tra applicazioni client-server,
in C# possiamo utilizzare:
Json.Encode() Json.Decode()
che troviamo nel namespace System.Web.Helpers
Con queste due funzioni possiamo rispettivamente serializzare e deserializzare un qualsiasi oggetto C# da e verso JSON.
La serializzazione standard prevede la creazione di stringhe JSON in forma di coppie chiave-valore.
Ad esempio, immaginiamo di avere la seguente classe C# che memorizza lo stato di gioco corrente in un ambiente multiplayer (ad esempio da dover inviare a tutti i client connessi ogni tot millisecondi):
private class GameSnapshot { public class EnemyPosition { public double x; public double y; public double direction; public int energy; } public string playerName; public double playerXpos; public double playerYpos; public int lives; public int level; public int points; public List<EnemyPosition> enemiesPositions; public DateTime timestamp; }
La lista enemiesPositions contiene le posizioni degli avversari, gli altri parametri sono quelli relativi al giocatore (posizione, stato, vite rimanenti etc..).
Una plausible conversione JSON dell’oggetto potrebbe essere la seguente:
{ "playerName": "unnamed player", "playerXpos": 12.124354328, "playerYpos": 3.8943532434, "lives": 4, "level": 12, "points": 81000, "enemiesPositions": [ { "x": 0.17568866497636246, "y": 0.8758737812172034, "direction": 0.5097326890145115, "energy": 1102696000 }, { "x": 0.26358662371690694, "y": 0.8292919941382911, "direction": 0.26260223484719275, "energy": 2123401755 }, { "x": 0.7504166782602746, "y": 0.5961088224296034, "direction": 0.5173756976227163, "energy": 1782593816 }, { "x": 0.4766914893298836, "y": 0.8596706198806272, "direction": 0.1079285652879293, "energy": 1907324897 }, { "x": 0.5031359705622941, "y": 0.13449626934458328, "direction": 0.21105524069213086, "energy": 1125504690 } ], "timestamp": "/Date(1437732572430)/" }
Per un totale di 923 byte, quasi 1K di dati da trasmettere. Non molti, ma dobbiamo considerare che i dati potrebbero dover essere trasmessi molte volte al secondo e che i client connessi potrebbero essere molti.
Di default la serializzazione prevede la creazione di coppie chiavi-valore dove chiave è il nome della variabile (es. “playerName”) e valore il suo valore corrispondente (es. “unnamed player”). Per una codifica più efficiente possiamo eliminare tutte le chiavi e lasciare solo i valori: la posizione relativa dei valori nella struttura dati JSON è sufficiente a ricostruire completamente (deserializzare) l’oggetto originale.
A tal fine possiam utilizzare il tipo dati dynamic, introdotto in C# 4.0. Dynamic è un tipo dati appunto dinamico, il compilatore ignora il tipo reale della variabile o lo pospone all’esecuzione. Creando una lista di dynamic possiamo creare sequenze di oggetti non omogenei tra loro (liste o array di tipi diversi) ognuno dei quali è anche anomimo (non ha un nome esplicito, è solo un elemento della lista).
La classe di prima diventa semplicemente una lista di tipi dynamic:
var gameShapshot = new List<dynamic>();
Possiamo aggiungere a questa lista (metodo .Add()) qualsiasi oggetto: un int, una striga, un DateTime, liste di altri oggetti e cosi’ via. Ad esempio, possiamo creare un oggetto dynamic che contiene le stesse informazioni della classe GameSnapshot vista in precedenza.
var gs = new List<dynamic>(); gs.Add("unnamed player"); gs.Add(12); gs.Add(4); gs.Add(81000); gs.Add(DateTime.Now); gs.Add(12.124354328); gs.Add(3.8943532434); var enemiesPositions = new List<dynamic>(); for (var i = 0; i < 5; i++) { var enemyPos = new List<dynamic>(); enemyPos.Add(rnd.NextDouble()); enemyPos.Add(rnd.NextDouble()); enemyPos.Add(rnd.NextDouble()); enemyPos.Add(rnd.Next()); enemiesPositions.Add(enemyPos); } gs.Add(enemiesPositions); String snapShot = Json.Encode(gs); System.Console.WriteLine(snapShot);
Abbiamo ora un oggetto C# che contiene le stesse informazioni (sia in valore che in struttura) ma la serializzazione JSON risulta molto più snella:
[ "unnamed player", 12, 4, 81000, "/Date(1437732515952)/", 12.124354328, 3.8943532434, [ [ 0.5849644861114046, 0.9460975015284948, 0.9270875434982998, 1612467919 ], [ 0.5084204606285414, 0.6327498385835205, 0.7234090821460863, 784917789 ], [ 0.8641532142014025, 0.2696795273896677, 0.21300438335770946, 1166561007 ], [ 0.6842819497381719, 0.10087060700211237, 0.3484976507483505, 686316417 ], [ 0.27328705707252354, 0.2811392360744715, 0.3885680466837101, 1938705260 ] ] ]
Occupa 650 byte, poco più della metà. Riducendo i decimali di default (spesso non serve tutta quella risoluzione, ovviamente dipende dal contesto) arriviamo a circa 400 byte. A parità di banda significa raddoppiare il numero di client connessi contemporaneamente, raddoppiare il rate di invio dei dati o una combinazione dei due.
Per lunghi array di valori numerici, ad esempio per dati relativi a grafici la serializzazione senza nomi risulta particolarmente efficiente e si adatta bene a linguaggi loosely typed come Javascript e PHP.
Ad esempio:
[ [ 0.5849644861114046, 0.9460975015284948, 0.9270875434982998, 0.8641532142014025 ], [ 0.5084204606285414, 0.6327498385835205, 0.7234090821460863, 0.2811392360744715 ], [ 0.8641532142014025, 0.2696795273896677, 0.21300438335770946, 0.2696795273896677 ], [ 0.6842819497381719, 0.10087060700211237, 0.3484976507483505, 0.9270875434982998 ], [ 0.27328705707252354, 0.2811392360744715, 0.3885680466837101, 0.6327498385835205 ], [ 0.8641532142014025, 0.2696795273896677, 0.21300438335770946, 0.2696795273896677 ] ]
Ho trovato la soluzione con i dynamic abbastanza utile e veloce da implementare ma credo ne esistano altre. BSON per esempio? Oppure Message Pack.
Leave a Reply