Serializzazione JSON in C#

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.

Creare uno splash screen in Android

Gli splash screen sono utilizzati per mostrare all’utente del testo o delle immagini statiche prima ancora del completo caricamento dell’applicazione, soprattutto quando il caricamento richiede alcuni secondi. Senza un buon splash screen l’utente vede semplicemente uno schermo nero: più di 4-5 secondi di nero possono far desistere l’utente dal continuare ad aspettare!

Di solito uno splash screen è semplicemente una immagine a tutto schermo che mostra il nome dell’applicazione, il logo, del testo o, nelle versioni più evolute, una barra di caricamento.

popular_apps_splash_screens

Realizzare uno splash screen in Android è abbastanza semplice.

Creiamo una nuova blank activity, chiamiamola ad esempio “start”:

Screen shot 2015-07-19 at 1.59.12 PM

Possiamo cambiare il colore dello sfondo, aggiungere qualsiasi immagine e/o testo preferiamo.

Impostiamo ora l’activity come activity principale. Sarà la prima ad essere lanciata all’avvio dell’applicazione. A tal fine modifichiamo il manifest.xml in questo modo:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="gianluca.connect4" >

    <application android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity android:name=".start"
            android:label="@string/app_name" >
            <intent -filter>
                <action android:name="android.intent.action.MAIN"></action>
                <category android:name="android.intent.category.LAUNCHER"></category>
            </intent>
        </activity>
    </application>
</manifest>

Ridefiniamo ora il metodo onCreate() nel sorgente start.java in questo modo:

public class start extends Activity
{
    int timeout = 2000;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_start);

        new Handler().postDelayed(new Runnable()
        {
            @Override
            public void run()
            {
                Intent i = new Intent(start.this, main.class);
                startActivity(i);
                finish();
            }
        }, timeout);
    }
}

Praticamente non facciamo altro che creare un nuovo oggetto Runnable (un nuovo thread) e ne posticipiamo l’esecuzione a 2 secondi dall’evento onCreate(). L’intent che andiamo a creare chiamerà l’activity main che nel nostro caso è la prossima schermata dell’applicazione.

Avviando l’applicazione vedremo lo splash screen per 2 secondi, poi verrà lanciata l’activity main.
Il tempo di attesa impostato per lo splash screen può essere sfruttato in vari modi:

  • Caricare oggetti locali particolarmente pesanti (immagini, file audio etc..)
  • Accedere a risorse in rete e scaricare dati da backend remoti
  • Avviare, riavviare, disabilitare device locali (wifi, bluetooth etc..)
  • Nessuna operazione particolare, solo mostrare il nome dell’app, i contatti etc..

AliceDroid – Wifi password recovery tool per Android

Ho creato questa semplice app, la versione Android del tool Alicekeygen. Esattamente come la versione desktop questa applicazione permette il recupero immediato della password wifi per le reti del tipo Alice-xxxxxxxx. I router supportati sono quelli della famiglia con firmware AGPF.

device-2015-07-03-220912

L’applicazione è stata realizzata con AndroidStudio, l’IDE ufficiale Google per lo sviluppo su piattaforma Android. L’algoritmo del tool originale è scritto in C, il porting in Java ha prodotto un algoritmo identico per funzionalità anche se differente nella forma (in Java mancano i puntatori, regole di casting diverse, etc..etc..).

Il tool originale era una semplice applicazione da riga di comando: per recuperare la password occorreva specificare l’SSID della rete, il MAC address della rete stessa e il file dei magic number. Utilizzare questa app è invece molto più semplice e intuitivo: le reti rilevate vengono semplicemente elencate nella schermata principale.

device-2015-07-03-223309

Le reti del tipo “Alice-xxxxxxxx” vengono colorate di rosso ad indicare che sono quelle supportate dall’applicazione. Selezionando una di esse verrà aperta la schermata delle password candidate da provare per quella rete:

device-2015-07-03-223320

 

Per provare una password è sufficiente copiarla nella clipboard (cliccandoci sopra) ed aprire le impostazioni di rete per effettuare l’accesso.

L’app è gratuita e disponibile in Google Play

googleplay

 

 

 

Algoritmo in Java

Questo il porting Java dell’algoritmo originale:

String seriesXserial = "" + String.format("%05d", series) + "X" + String.format("%07d", serialsSet[j]);

/* calcola l'hash SHA256(fixedPadding + serie-X-seriale + MAC) */

try
{
   MessageDigest digest = MessageDigest.getInstance("SHA-256");
   digest.update(fixedPadding, 0, fixedPadding.length);
   digest.update(seriesXserial.getBytes(), 0, seriesXserial.getBytes().length);
   digest.update(macAddr, 0, macAddr.length);
   byte[] hash = digest.digest();

   char[] wpa = new char[24];
   StringBuilder sb = new StringBuilder();
   for (int i = 0; i &lt; 24; i++)
   {
      int s = hash[i] &amp; 0xFF;
      wpa[i] = charset[s];
      sb.append(wpa[i]);
   }

   if (passwordList.contains(sb.toString()) == false)
   {
      passwordList.add(sb.toString());
   }
}
catch(Exception e)
{
    return false;
}