La libreria usata in questo brutale esempio è questa, XML-RPC for C and C++ , ed è necessario compilarla e installarla prima di compilare qualsiasi client o server basato su di essa ,
l'esempio è disponibile completamente qui : https://bitbucket.org/Forzaferrarileo/esempi-c-open-api-rsm
Come prova ho preso il metodo paline.Percorsi , che restituisce i percorsi di una determinata linea , passata come argomento alla chiamata ( in questo esempio è lo 056 ) , seguendo lo schema riportato nella wiki ufficiale : Descrizione paline.Percorsi
In particolare , la chiamata prende 3 argomenti :
- Un token generato dall'autenticazione al server
- Il numero/nome della linea ( si può anche passare ad esempio MEA , MEB , MEB1 , MEC per le linee metro )
- un codice della lingua , utilizzato per le info ( ad esempio se la linea è disattivata )
E "ufficialmente" restituisce un dizionario così formato ( ufficialmente perchè poi nella realtà e diversa ) :
- bool monitorata
- bool abilitata
- int id_news
- array percorsi
- string id_percorso
- string descrizione
- string capolinea
Cosa c'è di diverso dalla definizione alla risposta reale ?
{ id_richiesta :
risposta {
monitorata
abilitata
id_news
percorsi [
{
id_percorso
descrizione
capolinea
}
]
}
}
la seconda cosa come si può vedere , è che l'array percorsi è formato a sua volta da n strutture contenti i vari percorsi della linea.
La terza e ultima differenza è che l'elemento "monitorata" non è un valore booleano ma intero
Passando alla parte del codice in c++ ( il file completo lo trovare in questa repo su bitbucket ) basata sul client semplice fornito come esempio nella libreria , non è altro che scomposizione della risposta nelle varie strutture e elementi elencati sopra :
Omettendo i vari header , abbiamo la richiesta di autenticazione al server di RSM :
string const authServer("http://muovi.roma.it/ws/xml/autenticazione/1"); string const authMethod("autenticazione.Accedi"); xmlrpc_c::clientSimple authClient; xmlrpc_c::value result; authClient.call(authServer, authMethod, "ss", &result, DEV_KEY, ""); string str = xmlrpc_c::value_string(result); cout << "token: " << str << endl;
che segue esattamente l'esempio fornito dalla libreria ( :-) ) , con un diverso server , metodo , e diversi argomenti passati alla chiamata , ovvero la DEV_KEY , cioè la chiave sviluppatore generata registrandovi su muovi.roma , e un argomento nullo che a noi non serve ( se popolato è utile per identificare univocamente dispositivi come i cellulari , che sfruttano comunque la propria chiave sviluppatore )
la risposta è una stringa che contiene il token da utilizzare per la chiamata successiva
Adesso è il momento di richiamare il metodo paline.Percorsi , quindi come prima la strutture della classe del client rimane invariata , variando il server , il metodo e gli argomenti della chiamata
string const palineServer("http://muovi.roma.it/ws/xml/paline/7"); string const palineMethod("paline.Percorsi"); xmlrpc_c::clientSimple palineClient; xmlrpc_c::value paline; palineClient.call(palineServer, palineMethod, "sss", &paline, str.c_str() , "056", "it");
A questo punto inizia la parte divertente : la risposta salvata nella variabile paline viene restituita come map dalla funzione cvalue() , e quindi creeremo un map con chiavi di tipo stringa , e valori degli elementi xmlrpc_c::value , ovvero il dato arbitrario della libreria , chiamato mymap ( l'altro map uguale , risposta , ci servirà dopo )
map<string, xmlrpc_c::value> mymap , risposta ;
per far diventare la struttura ( la risposta ndr ) in un map , associo la risposta al valore xmlrpc_c::value_struct , e poi tramite il metodo cvalue() la faccio diventare un classico map c++.
A questo punto , posso già isolare l'elemento id_richiesta e mostrarlo sullo schermo
xmlrpc_c::value_struct palineStruct = xmlrpc_c::value_struct(paline); mymap = palineStruct.cvalue(); cout<< "id_richiesta : " << xmlrpc_c::value_string(mymap["id_richiesta"]).cvalue() << endl;
analogamente ripeto i passaggi precedenti per la struttura risposta
risposta = xmlrpc_c::value_struct(mymap["risposta"]).cvalue();
e mostro i valori dei vari elementi
//I tipi dei vari elementi sono fissati rigidamente seguendo il layout della risposta map<string, xmlrpc_c::value>::iterator it = risposta.begin(); cout<< it->first << " : " << xmlrpc_c::value_boolean(risposta["abilitata"]).cvalue() << endl; ++it; cout<< it->first << " : " << xmlrpc_c::value_int(risposta["id_news"]).cvalue() << endl; ++it; cout<< it->first << " : " << xmlrpc_c::value_int(risposta["monitorata"]).cvalue() << endl; ++it; cout<< it->first << " : " << endl;
A questo punto arriva l'array dei percorsi. La differenza da una struttura è che questo non avendo delle chiavi per identificare i vari elementi , viene riportato come un vettore. Questo vettore conterrà a sua volta altre strutture contenenti i dati dei vari percorsi.
Riapplicando le stesse operazioni fatte in precedenza si ha :
vector<xmlrpc_c::value> percorsi; percorsi = xmlrpc_c::value_array(risposta["percorsi"]).cvalue(); int numeroPercorsi = percorsi.size(); for( int i = 0; i < numeroPercorsi; i++){ map<string, xmlrpc_c::value> percorso = xmlrpc_c::value_struct(percorsi[i]).cvalue(); cout << " percorso " << i << " : " << endl; cout << " id_percorso: " << xmlrpc_c::value_string(percorso["id_percorso"]).cvalue() << endl; cout << " descrizione: " << xmlrpc_c::value_string(percorso["descrizione"]).cvalue() << endl; cout << " capolinea: " << xmlrpc_c::value_string(percorso["capolinea"]).cvalue() << endl; }